class TestRESTApi():
    """
    Unit tests for the OpenCog REST API.

    See: opencog/python/web/api/apimain.py for the class definitions
    Documentation: http://wiki.opencog.org/w/REST_API
    """

    def setUp(self):
        self.uri = '/api/v1.0/'
        self.headers = {'content-type': 'application/json'}

        # Populate a test AtomSpace
        self.atomspace = AtomSpace()
        self.animal = self.atomspace.add_node(types.ConceptNode, 'animal', TruthValue(.1, .9))
        self.bird = self.atomspace.add_node(types.ConceptNode, 'bird', TruthValue(.01, .9))
        self.swan = self.atomspace.add_node(types.ConceptNode, 'swan', TruthValue(.001, .9))
        self.swan_bird = self.atomspace.add_link(types.InheritanceLink, [self.swan, self.bird], TruthValue(1, .9))
        self.bird_animal = self.atomspace.add_link(types.InheritanceLink, [self.bird, self.animal], TruthValue(1, .9))
        self.bird.av = {'sti': 9}

        self.api = RESTAPI(self.atomspace)
        self.client = self.api.test()

    def tearDown(self):
        del self.api
        del self.client

    def test_post_and_get_node(self):
        # Create a test node
        truthvalue = {'type': 'simple', 'details': {'strength': 0.08, 'count': 0.2}}
        atom = {'type': 'ConceptNode', 'name': 'giant_frog', 'truthvalue': truthvalue}

        post_response = self.client.post(self.uri + 'atoms', data=json.dumps(atom), headers=self.headers)
        post_result = json.loads(post_response.data)['atoms']

        # Verify values returned by the POST request
        assert post_result['type'] == atom['type']
        assert post_result['name'] == atom['name']
        assert post_result['truthvalue']['type'] == truthvalue['type']
        assert_almost_equals(
            float(post_result['truthvalue']['details']['strength']), truthvalue['details']['strength'], places=5)
        assert_almost_equals(
            float(post_result['truthvalue']['details']['count']), truthvalue['details']['count'], places=5)

        # Compare to the values created in the AtomSpace
        atomspace_result = self.atomspace[Handle(post_result['handle'])]
        assert Handle(post_result['handle']) == atomspace_result.h
        assert post_result['name'] == atomspace_result.name
        assert types.__dict__.get(post_result['type']) == atomspace_result.type
        assert TruthValue(float(post_result['truthvalue']['details']['strength']),
                          float(post_result['truthvalue']['details']['count'])) == atomspace_result.tv

        # Get by handle and compare
        handle = post_result['handle']
        get_response_handle = self.client.get(self.uri + 'atoms/' + str(handle))
        get_result_handle = json.loads(get_response_handle.data)['atoms']
        assert post_result == get_result_handle

        # Get by name and compare
        name = post_result['name']
        get_response_name = self.client.get(self.uri + 'atoms?name=' + name)
        get_result_name = json.loads(get_response_name.data)['result']['atoms'][0]
        assert post_result == get_result_name

        # Get by name and type and compare
        type = post_result['type']
        get_response_name_type = self.client.get(self.uri + 'atoms?name=' + name + '&type=' + type)
        get_result_name_type = json.loads(get_response_name_type.data)['result']['atoms'][0]
        assert post_result == get_result_name_type

    def test_post_and_get_link(self):
        # Create a test link between swan and animal
        truthvalue = {'type': 'simple', 'details': {'strength': 0.5, 'count': 0.4}}
        atom = {'type': 'InheritanceLink', 'truthvalue': truthvalue, 'outgoing':
            [self.swan.h.value(), self.animal.h.value()]}

        post_response = self.client.post(self.uri + 'atoms', data=json.dumps(atom), headers=self.headers)
        post_result = json.loads(post_response.data)['atoms']

        # Verify values returned by the POST request
        assert post_result['type'] == atom['type']
        assert post_result['truthvalue']['type'] == truthvalue['type']
        assert_almost_equals(
            float(post_result['truthvalue']['details']['strength']), truthvalue['details']['strength'], places=5)
        assert_almost_equals(
            float(post_result['truthvalue']['details']['count']), truthvalue['details']['count'], places=5)
        assert self.swan.h.value() in post_result['outgoing']
        assert self.animal.h.value() in post_result['outgoing']

        # Compare to the values created in the AtomSpace
        atomspace_result = self.atomspace[Handle(post_result['handle'])]
        assert Handle(post_result['handle']) == atomspace_result.h
        assert types.__dict__.get(post_result['type']) == atomspace_result.type
        assert TruthValue(float(post_result['truthvalue']['details']['strength']),
                          float(post_result['truthvalue']['details']['count'])) == atomspace_result.tv

        # Get by handle and compare
        handle = post_result['handle']
        get_response_handle = self.client.get(self.uri + 'atoms/' + str(handle))
        get_result_handle = json.loads(get_response_handle.data)['atoms']
        assert post_result == get_result_handle

        # Check if the link is in the incoming set of each of the nodes
        for h in post_result['outgoing']:
            assert Handle(post_result['handle']) in [atom.h for atom in self.atomspace[Handle(h)].incoming]

    def test_put_and_get_tv_av_node(self):
        atom = self.swan
        truthvalue = {'type': 'simple', 'details': {'strength': 0.005, 'count': 0.8}}
        attentionvalue = {'sti': 9, 'lti': 2, 'vlti': True}
        atom_update = {'truthvalue': truthvalue, 'attentionvalue': attentionvalue}
        put_response = self.client.put(self.uri + 'atoms/' + str(atom.h.value()), data=json.dumps(atom_update), headers=self.headers)
        put_result = json.loads(put_response.data)['atoms']

        # Verify values returned by the PUT request
        assert put_result['handle'] == atom.h.value()
        assert_almost_equals(
            float(put_result['truthvalue']['details']['strength']), truthvalue['details']['strength'], places=5)
        assert_almost_equals(
            float(put_result['truthvalue']['details']['count']), truthvalue['details']['count'], places=5)
        assert put_result['attentionvalue']['sti'] == attentionvalue['sti']
        assert put_result['attentionvalue']['lti'] == attentionvalue['lti']
        assert put_result['attentionvalue']['vlti'] == attentionvalue['vlti']

        # Compare to the values updated in the AtomSpace
        atomspace_result = self.atomspace[Handle(put_result['handle'])]
        assert Handle(put_result['handle']) == atomspace_result.h
        assert types.__dict__.get(put_result['type']) == atomspace_result.type
        assert TruthValue(float(put_result['truthvalue']['details']['strength']),
                          float(put_result['truthvalue']['details']['count'])) == atomspace_result.tv
        assert put_result['attentionvalue'] == atomspace_result.av

        # Get by handle and compare
        get_response = self.client.get(self.uri + 'atoms/' + str(atom.h.value()))
        get_result = json.loads(get_response.data)['atoms']
        assert put_result == get_result

    def test_put_and_get_tv_av_link(self):
        atom = self.bird_animal
        truthvalue = {'type': 'simple', 'details': {'strength': 0.9, 'count': 0.95}}
        attentionvalue = {'sti': 6, 'lti': 3, 'vlti': True}
        atom_update = {'truthvalue': truthvalue, 'attentionvalue': attentionvalue}
        put_response =\
            self.client.put(self.uri + 'atoms/' + str(atom.h.value()), data=json.dumps(atom_update), headers=self.headers)
        put_result = json.loads(put_response.data)['atoms']

        # Verify values returned by the PUT request
        assert put_result['handle'] == atom.h.value()
        assert_almost_equals(
            float(put_result['truthvalue']['details']['strength']), truthvalue['details']['strength'], places=5)
        assert_almost_equals(
            float(put_result['truthvalue']['details']['count']), truthvalue['details']['count'], places=5)
        assert put_result['attentionvalue']['sti'] == attentionvalue['sti']
        assert put_result['attentionvalue']['lti'] == attentionvalue['lti']
        assert put_result['attentionvalue']['vlti'] == attentionvalue['vlti']

        # Compare to the values updated in the AtomSpace
        atomspace_result = self.atomspace[Handle(put_result['handle'])]
        assert Handle(put_result['handle']) == atomspace_result.h
        assert types.__dict__.get(put_result['type']) == atomspace_result.type
        assert TruthValue(float(put_result['truthvalue']['details']['strength']),
                          float(put_result['truthvalue']['details']['count'])) == atomspace_result.tv
        assert put_result['attentionvalue'] == atomspace_result.av

        # Get by handle and compare
        get_response = self.client.get(self.uri + 'atoms/' + str(atom.h.value()))
        get_result = json.loads(get_response.data)['atoms']
        assert put_result == get_result

    def test_post_revise_existing_node(self):
        # Attempt to create a node, where a node already exists with that name and type. Should revise existing node.
        existing_atom = self.bird

        truthvalue = {'type': 'simple', 'details': {'strength': 0.1, 'count': 0.95}}
        atom = {'type': 'ConceptNode', 'name': 'bird', 'truthvalue': truthvalue}

        post_response = self.client.post(self.uri + 'atoms', data=json.dumps(atom), headers=self.headers)
        post_result = json.loads(post_response.data)['atoms']

        # Verify values returned by the POST request
        assert_almost_equals(
            float(post_result['truthvalue']['details']['strength']), truthvalue['details']['strength'], places=5)
        assert_almost_equals(
            float(post_result['truthvalue']['details']['count']), truthvalue['details']['count'], places=5)

        # Compare to the values updated in the AtomSpace
        assert post_result['handle'] == existing_atom.h.value()
        assert TruthValue(float(post_result['truthvalue']['details']['strength']),
                          float(post_result['truthvalue']['details']['count'])) == existing_atom.tv

    def test_post_revise_existing_link(self):
        # Attempt to create a link, where a link already exists with that name and outgoing set.
        # Should revise existing link.
        existing_atom = self.bird_animal
        truthvalue = {'type': 'simple', 'details': {'strength': 0.1, 'count': 0.95}}
        outgoing = [a.h.value() for a in existing_atom.out]
        atom = {'type': 'InheritanceLink', 'truthvalue': truthvalue, 'outgoing': outgoing}

        post_response = self.client.post(self.uri + 'atoms', data=json.dumps(atom), headers=self.headers)
        post_result = json.loads(post_response.data)['atoms']

        # Verify values returned by the POST request
        assert post_result['outgoing'] == outgoing
        assert_almost_equals(
            float(post_result['truthvalue']['details']['strength']), truthvalue['details']['strength'], places=5)
        assert_almost_equals(
            float(post_result['truthvalue']['details']['count']), truthvalue['details']['count'], places=5)

        # Compare to the values updated in the AtomSpace
        assert post_result['handle'] == existing_atom.h.value()
        assert TruthValue(float(post_result['truthvalue']['details']['strength']),
                          float(post_result['truthvalue']['details']['count'])) == existing_atom.tv

    @raises(IndexError)
    def test_delete_node(self):
        atom = self.swan
        handle = atom.h.value()
        get_response = self.client.get(self.uri + 'atoms/' + str(handle))
        get_result = json.loads(get_response.data)['atoms']

        delete_response = self.client.delete(self.uri + 'atoms/' + str(atom.h.value()))
        delete_result = json.loads(delete_response.data)['result']

        assert delete_result['success']
        assert delete_result['handle'] == get_result['handle']

        # Confirm the atom isn't contained in the AtomSpace anymore
        assert_raises(self.atomspace[atom.h], IndexError)

    @raises(IndexError)
    def test_delete_link(self):
        atom = self.bird_animal
        handle = atom.h.value()
        get_response = self.client.get(self.uri + 'atoms/' + str(handle))
        get_result = json.loads(get_response.data)['atoms']

        delete_response = self.client.delete(self.uri + 'atoms/' + str(atom.h.value()))
        delete_result = json.loads(delete_response.data)['result']

        assert delete_result['success']
        assert delete_result['handle'] == get_result['handle']

        # Confirm the atom isn't contained in the AtomSpace anymore
        assert_raises(self.atomspace[atom.h], IndexError)

    def test_get_atoms_by_av(self):
        # Assign some STI values
        self.atomspace.set_av(h=self.bird.h, sti=9)
        self.atomspace.set_av(h=self.swan.h, sti=20)
        self.atomspace.set_av(h=self.bird_animal.h, sti=15)
        self.atomspace.set_av(h=self.animal.h, sti=0)

        get_response = self.client.get(self.uri + 'atoms?filterby=attentionalfocus')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 3
        assert set([atom['handle'] for atom in get_result]) == set([self.bird.h.value(), self.swan.h.value(), self.bird_animal.h.value()])

        get_response = self.client.get(self.uri + 'atoms?filterby=stirange&stimin=15')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 2
        assert set([atom['handle'] for atom in get_result]) == set([self.swan.h.value(), self.bird_animal.h.value()])

        get_response = self.client.get(self.uri + 'atoms?filterby=stirange&stimin=15&stimax=18')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 1
        assert set([atom['handle'] for atom in get_result]) == set([self.bird_animal.h.value()])
Beispiel #2
0
class TestRESTApi():
    """
    Unit tests for the OpenCog REST API.

    See: opencog/python/web/api/apimain.py for the class definitions
    Documentation: http://wiki.opencog.org/w/REST_API
    """
    def setUp(self):
        self.uri = '/api/v1.1/'
        self.headers = {'content-type': 'application/json'}

        # Populate a test AtomSpace
        self.atomspace = AtomSpace()
        self.animal = self.atomspace.add_node(
            types.ConceptNode, 'animal', TruthValue(.1, 0.0011860914528369904))
        self.bird = self.atomspace.add_node(
            types.ConceptNode, 'bird', TruthValue(.01, 0.0011237357975915074))
        self.swan = self.atomspace.add_node(
            types.ConceptNode, 'swan', TruthValue(.001, 0.0011237357975915074))
        self.frog = self.atomspace.add_node(
            types.ConceptNode, 'frog', TruthValue(.001, 0.7142857313156128))
        self.swan_bird = self.atomspace.add_link(
            types.InheritanceLink, [self.swan, self.bird],
            TruthValue(1, 0.0011237357975915074))
        self.bird_animal = self.atomspace.add_link(
            types.InheritanceLink, [self.bird, self.animal],
            TruthValue(1, 0.0011237357975915074))
        self.bird.sti = 9
        self.swan.sti = 9

        self.api = RESTAPI(self.atomspace)
        self.client = self.api.test()

    def tearDown(self):
        del self.api
        del self.client

    def mkatom(self, json_atom):
        post_response = self.client.post(self.uri + 'atoms',
                                         data=json.dumps(json_atom),
                                         headers=self.headers)
        post_result = json.loads(post_response.data)['atoms']
        return post_result

    def mkswan(self):
        return self.mkatom({
            'type': 'ConceptNode',
            'name': 'swan',
            'truthvalue': {
                'type': 'simple',
                'details': {
                    'strength': 0.001,
                    'count': 0.9
                }
            }
        })

    def mkbird(self):
        return self.mkatom({
            'type': 'ConceptNode',
            'name': 'bird',
            'truthvalue': {
                'type': 'simple',
                'details': {
                    'strength': 0.01,
                    'count': 0.9
                }
            }
        })

    def mkanimal(self):
        return self.mkatom({
            'type': 'ConceptNode',
            'name': 'animal',
            'truthvalue': {
                'type': 'simple',
                'details': {
                    'strength': 0.1,
                    'count': 0.95
                }
            }
        })

    def mkbird_animal(self):
        jbird = self.mkbird()
        janimal = self.mkanimal()
        return self.mkatom({
            'type': 'InheritanceLink',
            'truthvalue': {
                'type': 'simple',
                'details': {
                    'strength': 1.0,
                    'count': 0.9
                }
            },
            'outgoing': [jbird['handle'], janimal['handle']]
        })

    def get_atom(self, handle):
        get_response_handle = \
            self.client.get(self.uri + 'atoms/' + str(handle))
        get_result_handle = \
            json.loads(get_response_handle.data)['result']['atoms'][0]
        return get_result_handle

    def test_a_post_and_get_node(self):
        # Create a test node
        truthvalue = {
            'type': 'simple',
            'details': {
                'strength': 0.08,
                'count': 0.2
            }
        }
        jatom = {
            'type': 'ConceptNode',
            'name': 'giant_frog',
            'truthvalue': truthvalue
        }

        post_response = self.client.post(self.uri + 'atoms',
                                         data=json.dumps(jatom),
                                         headers=self.headers)
        post_result = json.loads(post_response.data)['atoms']

        # Verify values returned by the POST request
        assert post_result['type'] == jatom['type']
        assert post_result['name'] == jatom['name']
        assert post_result['truthvalue']['type'] == truthvalue['type']
        assert_almost_equals(float(
            post_result['truthvalue']['details']['strength']),
                             truthvalue['details']['strength'],
                             places=5)
        assert_almost_equals(float(
            post_result['truthvalue']['details']['count']),
                             truthvalue['details']['count'],
                             places=5)

        # Compare to the values created in the AtomSpace
        frog = self.atomspace.add_node(
            types.ConceptNode, 'giant_frog',
            TruthValue(.08, count_to_confidence(0.2)))
        atomspace_result = frog
        assert post_result['name'] == atomspace_result.name
        assert types.__dict__.get(post_result['type']) == atomspace_result.type
        assert TruthValue(
            float(post_result['truthvalue']['details']['strength']),
            count_to_confidence(
                float(post_result['truthvalue']['details']['count']))) \
            == atomspace_result.tv

        # Get by handle and compare
        handle = post_result['handle']
        get_response_handle = \
            self.client.get(self.uri + 'atoms/' + str(handle))
        get_result_handle = \
            json.loads(get_response_handle.data)['result']['atoms'][0]
        assert post_result == get_result_handle

        # Get by name and compare
        name = post_result['name']
        get_response_name = self.client.get(self.uri + 'atoms?name=' + name)
        get_result_name = \
            json.loads(get_response_name.data)['result']['atoms'][0]
        assert post_result == get_result_name

        # Get by name and type and compare
        type = post_result['type']
        get_response_name_type = \
            self.client.get(self.uri + 'atoms?name=' + name + '&type=' + type)
        get_result_name_type = \
            json.loads(get_response_name_type.data)['result']['atoms'][0]
        assert post_result == get_result_name_type

    def test_b_post_and_get_link(self):
        jswan = self.mkswan()
        janimal = self.mkanimal()

        # Create a test link between swan and animal
        truthvalue = \
            {'type': 'simple', 'details': {'strength': 0.5, 'count': 0.4}}
        atom = {
            'type': 'InheritanceLink',
            'truthvalue': truthvalue,
            'outgoing': [jswan['handle'], janimal['handle']]
        }

        post_response = self.client.post(self.uri + 'atoms',
                                         data=json.dumps(atom),
                                         headers=self.headers)
        post_result = json.loads(post_response.data)['atoms']

        # Verify values returned by the POST request
        assert post_result['type'] == atom['type']
        assert post_result['truthvalue']['type'] == truthvalue['type']
        assert_almost_equals(float(
            post_result['truthvalue']['details']['strength']),
                             truthvalue['details']['strength'],
                             places=5)
        assert_almost_equals(float(
            post_result['truthvalue']['details']['count']),
                             truthvalue['details']['count'],
                             places=5)
        assert jswan['handle'] in post_result['outgoing']
        assert janimal['handle'] in post_result['outgoing']

        # Compare to the values created in the AtomSpace
        swan_animal = self.atomspace.add_link(
            types.InheritanceLink, [self.swan, self.animal],
            TruthValue(0.5, count_to_confidence(0.4)))

        atomspace_result = swan_animal
        assert types.__dict__.get(post_result['type']) == atomspace_result.type
        assert TruthValue(
            float(post_result['truthvalue']['details']['strength']),
            count_to_confidence(
                float(post_result['truthvalue']['details']['count']))) \
            == atomspace_result.tv

        # Get by handle and compare
        handle = post_result['handle']
        get_response_handle = \
            self.client.get(self.uri + 'atoms/' + str(handle))
        get_result_handle = \
            json.loads(get_response_handle.data)['result']['atoms'][0]
        assert post_result == get_result_handle

        # Check if the link is in the incoming set of each of the nodes
        jswan = self.get_atom(jswan['handle'])
        janimal = self.get_atom(janimal['handle'])
        for h in post_result['outgoing']:
            assert post_result['handle'] in jswan['incoming']
            assert post_result['handle'] in janimal['incoming']

    def test_c_put_and_get_tv_av_node(self):
        jswan = self.mkswan()

        truthvalue = \
            {'type': 'simple', 'details': {'strength': 0.005, 'count': 0.8}}
        attentionvalue = {'sti': 9, 'lti': 2, 'vlti': True}
        atom_update = \
            {'truthvalue': truthvalue, 'attentionvalue': attentionvalue}
        put_response = self.client.put(self.uri + 'atoms/' +
                                       str(jswan['handle']),
                                       data=json.dumps(atom_update),
                                       headers=self.headers)
        put_result = json.loads(put_response.data)['atoms']

        # Verify values returned by the PUT request
        assert put_result['handle'] == jswan['handle']
        assert_almost_equals(float(
            put_result['truthvalue']['details']['strength']),
                             truthvalue['details']['strength'],
                             places=5)
        assert_almost_equals(float(
            put_result['truthvalue']['details']['count']),
                             truthvalue['details']['count'],
                             places=5)
        assert put_result['attentionvalue']['sti'] == attentionvalue['sti']
        assert put_result['attentionvalue']['lti'] == attentionvalue['lti']
        assert put_result['attentionvalue']['vlti'] == attentionvalue['vlti']

        # Compare to the values updated in the AtomSpace
        atomspace_result = self.swan
        assert types.__dict__.get(put_result['type']) == atomspace_result.type
        assert TruthValue(
            float(put_result['truthvalue']['details']['strength']),
            count_to_confidence(
                float(put_result['truthvalue']['details']['count']))) \
            == atomspace_result.tv
        assert put_result['attentionvalue'] == atomspace_result.av

        # Get by handle and compare
        get_response = \
            self.client.get(self.uri + 'atoms/' + str(jswan['handle']))
        get_result = json.loads(get_response.data)['result']['atoms'][0]
        assert put_result == get_result

    def test_d_put_and_get_tv_av_link(self):
        jatom = self.mkbird_animal()
        truthvalue = \
            {'type': 'simple', 'details': {'strength': 0.9, 'count': 0.95}}
        attentionvalue = {'sti': 6, 'lti': 3, 'vlti': True}
        atom_update = \
            {'truthvalue': truthvalue, 'attentionvalue': attentionvalue}
        put_response =\
            self.client.put(
                self.uri + 'atoms/' + str(jatom['handle']),
                data=json.dumps(atom_update),
                headers=self.headers)
        put_result = json.loads(put_response.data)['atoms']

        # Verify values returned by the PUT request
        assert put_result['handle'] == jatom['handle']
        assert_almost_equals(float(
            put_result['truthvalue']['details']['strength']),
                             truthvalue['details']['strength'],
                             places=5)
        assert_almost_equals(float(
            put_result['truthvalue']['details']['count']),
                             truthvalue['details']['count'],
                             places=5)
        assert put_result['attentionvalue']['sti'] == attentionvalue['sti']
        assert put_result['attentionvalue']['lti'] == attentionvalue['lti']
        assert put_result['attentionvalue']['vlti'] == attentionvalue['vlti']

        # Compare to the values updated in the AtomSpace
        atomspace_result = self.bird_animal
        assert types.__dict__.get(put_result['type']) == atomspace_result.type
        assert TruthValue(
            float(put_result['truthvalue']['details']['strength']),
            count_to_confidence(
                float(put_result['truthvalue']['details']['count']))) \
            == atomspace_result.tv
        assert put_result['attentionvalue'] == atomspace_result.av

        # Get by handle and compare
        get_response = \
            self.client.get(self.uri + 'atoms/' + str(jatom['handle']))
        get_result = json.loads(get_response.data)['result']['atoms'][0]
        assert put_result == get_result

    def test_e_post_revise_existing_node(self):
        # Attempt to create a node, where a node already exists with that name
        # and type. Should revise existing node.
        existing_atom = self.bird

        truthvalue = \
            {'type': 'simple', 'details': {'strength': 0.1, 'count': 0.95}}
        atom = \
            {'type': 'ConceptNode', 'name': 'bird', 'truthvalue': truthvalue}

        post_response = self.client.post(self.uri + 'atoms',
                                         data=json.dumps(atom),
                                         headers=self.headers)
        post_result = json.loads(post_response.data)['atoms']

        # Verify values returned by the POST request
        assert_almost_equals(float(
            post_result['truthvalue']['details']['strength']),
                             truthvalue['details']['strength'],
                             places=5)
        assert_almost_equals(float(
            post_result['truthvalue']['details']['count']),
                             truthvalue['details']['count'],
                             places=5)

        # Compare to the values updated in the AtomSpace
        assert TruthValue(
            float(post_result['truthvalue']['details']['strength']),
            count_to_confidence(
                float(post_result['truthvalue']['details']['count']))) \
               == existing_atom.tv

    def test_f_post_revise_existing_link(self):
        # Attempt to create a link, where a link already exists with that name
        # and outgoing set.
        # Should revise existing link.
        existing_atom = self.bird_animal
        jbird_animal = self.mkbird_animal()
        truthvalue = \
            {'type': 'simple', 'details': {'strength': 0.1, 'count': 0.95}}
        outgoing = jbird_animal['outgoing']
        atom = {
            'type': 'InheritanceLink',
            'truthvalue': truthvalue,
            'outgoing': outgoing
        }

        post_response = self.client.post(self.uri + 'atoms',
                                         data=json.dumps(atom),
                                         headers=self.headers)
        post_result = json.loads(post_response.data)['atoms']

        # Verify values returned by the POST request
        assert post_result['outgoing'] == outgoing
        assert_almost_equals(float(
            post_result['truthvalue']['details']['strength']),
                             truthvalue['details']['strength'],
                             places=5)
        assert_almost_equals(float(
            post_result['truthvalue']['details']['count']),
                             truthvalue['details']['count'],
                             places=5)

        # Compare to the values updated in the AtomSpace
        assert TruthValue(
            float(post_result['truthvalue']['details']['strength']),
            count_to_confidence(
                float(post_result['truthvalue']['details']['count']))) \
               == existing_atom.tv

    # @raises(IndexError)
    def test_g_delete_node(self):
        jswan = self.mkswan()
        handle = jswan['handle']
        get_response = self.client.get(self.uri + 'atoms/' + str(handle))
        get_result = json.loads(get_response.data)['result']['atoms'][0]

        old_size = self.atomspace.size()
        delete_response = \
            self.client.delete(self.uri + 'atoms/' + str(handle))
        delete_result = json.loads(delete_response.data)['result']

        assert delete_result['success']
        assert delete_result['handle'] == get_result['handle']

        # Confirm the atom isn't contained in the AtomSpace anymore
        # Actually, the python bindings don't provide a way to ask
        # this question. So we check the size, instead.  Should shrink
        # by 2, since both the swan, and the inhertance link get
        # deleted.
        new_size = self.atomspace.size()
        assert new_size + 2 == old_size

    # @raises(IndexError)
    def test_h_delete_link(self):
        jatom = self.mkbird_animal()
        handle = jatom['handle']
        get_response = self.client.get(self.uri + 'atoms/' + str(handle))
        get_result = json.loads(get_response.data)['result']['atoms'][0]

        old_size = self.atomspace.size()
        delete_response = \
            self.client.delete(self.uri + 'atoms/' + str(handle))
        delete_result = json.loads(delete_response.data)['result']

        assert delete_result['success']
        assert delete_result['handle'] == get_result['handle']

        # Confirm the atom isn't contained in the AtomSpace anymore
        # Actually, the python bindings don't provide a way to ask
        # this question. So we check the size, instead.  Should shrink
        # by 1, since only the inhertance link gets deleted.
        new_size = self.atomspace.size()
        assert new_size + 1 == old_size

    def test_i_get_atoms_by_av(self):
        # Assign some STI values
        self.bird.sti = 9
        self.swan.sti = 20
        self.bird_animal.sti = 15
        self.animal.sti = 0

        jswan = self.mkswan()
        jbird = self.mkbird()
        jbird_animal = self.mkbird_animal()
        get_response = \
            self.client.get(self.uri + 'atoms?filterby=attentionalfocus')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 3
        assert set([atom['handle'] for atom in get_result]) \
            == {jbird['handle'],
                jswan['handle'],
                jbird_animal['handle']}

        get_response = \
            self.client.get(self.uri + 'atoms?filterby=stirange&stimin=15')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 2
        assert set([atom['handle'] for atom in get_result]) \
            == {jswan['handle'], jbird_animal['handle']}

        get_response = self.client.get(
            self.uri + 'atoms?filterby=stirange&stimin=15&stimax=18')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 1
        assert set([atom['handle'] for atom in get_result]) \
            == {jbird_animal['handle']}

    def test_j_get_types(self):
        # Verify that a list of valid atom types was returned
        get_response = self.client.get(self.uri + 'types')
        get_result = json.loads(get_response.data)['types']
        assert len(get_result) > 0
        assert get_result.__contains__('ConceptNode')

    def test_k_tv_filter(self):
        # Should return animal, swan_bird, bird_animal (3 atoms)
        get_response = self.client.get(self.uri + 'atoms?tvStrengthMin=0.1')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 3

        # Should return frog (1 atom)
        get_response = self.client.get(self.uri + 'atoms?tvConfidenceMin=0.7')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 1

        # Should return frog (1 atom)
        get_response = self.client.get(self.uri + 'atoms?tvCountMin=2000')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 1

    def test_l_include_incoming_outgoing(self):
        # Should return bird and swan (2 atoms)
        get_response = self.client.get(
            self.uri +
            'atoms?filterby=stirange&stimin=1&includeIncoming=false')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 2

        # Should additionally return swan_bird and bird_animal (4 atoms)
        get_response = self.client.get(
            self.uri + 'atoms?filterby=stirange&stimin=1&includeIncoming=true')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 4

        # Should additionally return animal (5 atoms)
        get_response = \
            self.client.get(
                self.uri + 'atoms?filterby=stirange&stimin=1&'
                           'includeIncoming=true&includeOutgoing=true')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 5

    def test_m_scheme_command(self):
        # Test an arbitrary Scheme command to ensure the binding is working
        # properly

        # Set the AttentionalFocus Boundary
        command = {'command': '(cog-set-af-boundary! 5)'}

        post_response = self.client.post(self.uri + 'scheme',
                                         data=json.dumps(command),
                                         headers=self.headers)
        post_result = json.loads(post_response.data)['response']

        # Verify that the value returned by the POST request is the new
        # value of the AttentionalFocus Boundary
        assert post_result == "5\n"

        # Get the AttentionalFocus Boundary
        command = {'command': '(cog-af-boundary)'}

        post_response = self.client.post(self.uri + 'scheme',
                                         data=json.dumps(command),
                                         headers=self.headers)
        post_result = json.loads(post_response.data)['response']

        # Verify that it matches the previous response
        assert post_result == "5\n"

    def test_n_dot_export(self):
        # Export the atomspace to DOT format and ensure that there is a
        # properly defined DOT header created and the correct atoms are
        # included in the description

        # TODO: The Python module "graphviz" needs to be added to ocpkg, so
        # that this dependency will be available for the continuous integration
        # system
        try:
            from graph_description import dot

            get_response = self.client.get(
                self.uri + 'atoms?filterby=attentionalfocus&dot=True')
            get_result = json.loads(get_response.data)['result']
            assert get_result.startswith("// OpenCog Graph")
            assert "digraph" in get_result
            assert "swan" in get_result
            assert "bird" in get_result
            assert get_result.count("label") == 2
        except ImportError:
            pass
Beispiel #3
0
class TestRESTApi():
    """
    Unit tests for the OpenCog REST API.

    See: opencog/python/web/api/apimain.py for the class definitions
    Documentation: http://wiki.opencog.org/w/REST_API
    """

    def setUp(self):
        self.uri = '/api/v1.1/'
        self.headers = {'content-type': 'application/json'}

        # Populate a test AtomSpace
        self.atomspace = AtomSpace()
        self.animal = self.atomspace.add_node(
            types.ConceptNode, 'animal', TruthValue(.1, .95))
        self.bird = self.atomspace.add_node(
            types.ConceptNode, 'bird', TruthValue(.01, .9))
        self.swan = self.atomspace.add_node(
            types.ConceptNode, 'swan', TruthValue(.001, .9))
        self.frog = self.atomspace.add_node(
            types.ConceptNode, 'frog', TruthValue(.001, 2000))
        self.swan_bird = self.atomspace.add_link(
            types.InheritanceLink, [self.swan, self.bird], TruthValue(1, .9))
        self.bird_animal = self.atomspace.add_link(
            types.InheritanceLink, [self.bird, self.animal], TruthValue(1, .9))
        self.bird.av = {'sti': 9}
        self.swan.av = {'sti': 9}

        self.api = RESTAPI(self.atomspace)
        self.client = self.api.test()

    def tearDown(self):
        del self.api
        del self.client

    def test_post_and_get_node(self):
        # Create a test node
        truthvalue = {'type': 'simple',
                      'details': {'strength': 0.08, 'count': 0.2}}
        atom = {'type': 'ConceptNode',
                'name': 'giant_frog',
                'truthvalue': truthvalue}

        post_response = self.client.post(self.uri + 'atoms',
                                         data=json.dumps(atom),
                                         headers=self.headers)
        post_result = json.loads(post_response.data)['atoms']

        # Verify values returned by the POST request
        assert post_result['type'] == atom['type']
        assert post_result['name'] == atom['name']
        assert post_result['truthvalue']['type'] == truthvalue['type']
        assert_almost_equals(
            float(post_result['truthvalue']['details']['strength']),
            truthvalue['details']['strength'], places=5)
        assert_almost_equals(
            float(post_result['truthvalue']['details']['count']),
            truthvalue['details']['count'], places=5)

        # Compare to the values created in the AtomSpace
        atomspace_result = self.atomspace[Handle(post_result['handle'])]
        assert Handle(post_result['handle']) == atomspace_result.h
        assert post_result['name'] == atomspace_result.name
        assert types.__dict__.get(post_result['type']) == atomspace_result.type
        assert TruthValue(
            float(post_result['truthvalue']['details']['strength']),
            float(post_result['truthvalue']['details']['count'])) \
            == atomspace_result.tv

        # Get by handle and compare
        handle = post_result['handle']
        get_response_handle = \
            self.client.get(self.uri + 'atoms/' + str(handle))
        get_result_handle = \
            json.loads(get_response_handle.data)['result']['atoms'][0]
        assert post_result == get_result_handle

        # Get by name and compare
        name = post_result['name']
        get_response_name = self.client.get(self.uri + 'atoms?name=' + name)
        get_result_name = \
            json.loads(get_response_name.data)['result']['atoms'][0]
        assert post_result == get_result_name

        # Get by name and type and compare
        type = post_result['type']
        get_response_name_type = \
            self.client.get(self.uri + 'atoms?name=' + name + '&type=' + type)
        get_result_name_type = \
            json.loads(get_response_name_type.data)['result']['atoms'][0]
        assert post_result == get_result_name_type

    def test_post_and_get_link(self):
        # Create a test link between swan and animal
        truthvalue = \
            {'type': 'simple', 'details': {'strength': 0.5, 'count': 0.4}}
        atom = {'type': 'InheritanceLink', 'truthvalue': truthvalue,
                'outgoing': [self.swan.h.value(), self.animal.h.value()]}

        post_response = self.client.post(
            self.uri + 'atoms', data=json.dumps(atom), headers=self.headers)
        post_result = json.loads(post_response.data)['atoms']

        # Verify values returned by the POST request
        assert post_result['type'] == atom['type']
        assert post_result['truthvalue']['type'] == truthvalue['type']
        assert_almost_equals(
            float(post_result['truthvalue']['details']['strength']),
            truthvalue['details']['strength'], places=5)
        assert_almost_equals(
            float(post_result['truthvalue']['details']['count']),
            truthvalue['details']['count'], places=5)
        assert self.swan.h.value() in post_result['outgoing']
        assert self.animal.h.value() in post_result['outgoing']

        # Compare to the values created in the AtomSpace
        atomspace_result = self.atomspace[Handle(post_result['handle'])]
        assert Handle(post_result['handle']) == atomspace_result.h
        assert types.__dict__.get(post_result['type']) == atomspace_result.type
        assert TruthValue(
            float(post_result['truthvalue']['details']['strength']),
            float(post_result['truthvalue']['details']['count'])) \
            == atomspace_result.tv

        # Get by handle and compare
        handle = post_result['handle']
        get_response_handle = \
            self.client.get(self.uri + 'atoms/' + str(handle))
        get_result_handle = \
            json.loads(get_response_handle.data)['result']['atoms'][0]
        assert post_result == get_result_handle

        # Check if the link is in the incoming set of each of the nodes
        for h in post_result['outgoing']:
            assert Handle(post_result['handle']) \
                in [atom.h for atom in self.atomspace[Handle(h)].incoming]

    def test_put_and_get_tv_av_node(self):
        atom = self.swan
        truthvalue = \
            {'type': 'simple', 'details': {'strength': 0.005, 'count': 0.8}}
        attentionvalue = {'sti': 9, 'lti': 2, 'vlti': True}
        atom_update = \
            {'truthvalue': truthvalue, 'attentionvalue': attentionvalue}
        put_response = self.client.put(
            self.uri + 'atoms/' + str(atom.h.value()),
            data=json.dumps(atom_update),
            headers=self.headers)
        put_result = json.loads(put_response.data)['atoms']

        # Verify values returned by the PUT request
        assert put_result['handle'] == atom.h.value()
        assert_almost_equals(
            float(put_result['truthvalue']['details']['strength']),
            truthvalue['details']['strength'], places=5)
        assert_almost_equals(
            float(put_result['truthvalue']['details']['count']),
            truthvalue['details']['count'], places=5)
        assert put_result['attentionvalue']['sti'] == attentionvalue['sti']
        assert put_result['attentionvalue']['lti'] == attentionvalue['lti']
        assert put_result['attentionvalue']['vlti'] == attentionvalue['vlti']

        # Compare to the values updated in the AtomSpace
        atomspace_result = self.atomspace[Handle(put_result['handle'])]
        assert Handle(put_result['handle']) == atomspace_result.h
        assert types.__dict__.get(put_result['type']) == atomspace_result.type
        assert TruthValue(
            float(put_result['truthvalue']['details']['strength']),
            float(put_result['truthvalue']['details']['count'])) \
            == atomspace_result.tv
        assert put_result['attentionvalue'] == atomspace_result.av

        # Get by handle and compare
        get_response = \
            self.client.get(self.uri + 'atoms/' + str(atom.h.value()))
        get_result = json.loads(get_response.data)['result']['atoms'][0]
        assert put_result == get_result

    def test_put_and_get_tv_av_link(self):
        atom = self.bird_animal
        truthvalue = \
            {'type': 'simple', 'details': {'strength': 0.9, 'count': 0.95}}
        attentionvalue = {'sti': 6, 'lti': 3, 'vlti': True}
        atom_update = \
            {'truthvalue': truthvalue, 'attentionvalue': attentionvalue}
        put_response =\
            self.client.put(
                self.uri + 'atoms/' + str(atom.h.value()),
                data=json.dumps(atom_update),
                headers=self.headers)
        put_result = json.loads(put_response.data)['atoms']

        # Verify values returned by the PUT request
        assert put_result['handle'] == atom.h.value()
        assert_almost_equals(
            float(put_result['truthvalue']['details']['strength']),
            truthvalue['details']['strength'], places=5)
        assert_almost_equals(
            float(put_result['truthvalue']['details']['count']),
            truthvalue['details']['count'], places=5)
        assert put_result['attentionvalue']['sti'] == attentionvalue['sti']
        assert put_result['attentionvalue']['lti'] == attentionvalue['lti']
        assert put_result['attentionvalue']['vlti'] == attentionvalue['vlti']

        # Compare to the values updated in the AtomSpace
        atomspace_result = self.atomspace[Handle(put_result['handle'])]
        assert Handle(put_result['handle']) == atomspace_result.h
        assert types.__dict__.get(put_result['type']) == atomspace_result.type
        assert TruthValue(
            float(put_result['truthvalue']['details']['strength']),
            float(put_result['truthvalue']['details']['count'])) \
            == atomspace_result.tv
        assert put_result['attentionvalue'] == atomspace_result.av

        # Get by handle and compare
        get_response = \
            self.client.get(self.uri + 'atoms/' + str(atom.h.value()))
        get_result = json.loads(get_response.data)['result']['atoms'][0]
        assert put_result == get_result

    def test_post_revise_existing_node(self):
        # Attempt to create a node, where a node already exists with that name
        # and type. Should revise existing node.
        existing_atom = self.bird

        truthvalue = \
            {'type': 'simple', 'details': {'strength': 0.1, 'count': 0.95}}
        atom = \
            {'type': 'ConceptNode', 'name': 'bird', 'truthvalue': truthvalue}

        post_response = self.client.post(self.uri + 'atoms',
                                         data=json.dumps(atom),
                                         headers=self.headers)
        post_result = json.loads(post_response.data)['atoms']

        # Verify values returned by the POST request
        assert_almost_equals(
            float(post_result['truthvalue']['details']['strength']),
            truthvalue['details']['strength'], places=5)
        assert_almost_equals(
            float(post_result['truthvalue']['details']['count']),
            truthvalue['details']['count'], places=5)

        # Compare to the values updated in the AtomSpace
        assert post_result['handle'] == existing_atom.h.value()
        assert TruthValue(
            float(post_result['truthvalue']['details']['strength']),
            float(post_result['truthvalue']['details']['count'])) \
               == existing_atom.tv

    def test_post_revise_existing_link(self):
        # Attempt to create a link, where a link already exists with that name
        # and outgoing set.
        # Should revise existing link.
        existing_atom = self.bird_animal
        truthvalue = \
            {'type': 'simple', 'details': {'strength': 0.1, 'count': 0.95}}
        outgoing = [a.h.value() for a in existing_atom.out]
        atom = {'type': 'InheritanceLink',
                'truthvalue': truthvalue,
                'outgoing': outgoing}

        post_response = self.client.post(
            self.uri + 'atoms', data=json.dumps(atom), headers=self.headers)
        post_result = json.loads(post_response.data)['atoms']

        # Verify values returned by the POST request
        assert post_result['outgoing'] == outgoing
        assert_almost_equals(
            float(post_result['truthvalue']['details']['strength']),
            truthvalue['details']['strength'], places=5)
        assert_almost_equals(
            float(post_result['truthvalue']['details']['count']),
            truthvalue['details']['count'], places=5)

        # Compare to the values updated in the AtomSpace
        assert post_result['handle'] == existing_atom.h.value()
        assert TruthValue(
            float(post_result['truthvalue']['details']['strength']),
            float(post_result['truthvalue']['details']['count'])) \
               == existing_atom.tv

    @raises(IndexError)
    def test_delete_node(self):
        atom = self.swan
        handle = atom.h.value()
        get_response = self.client.get(self.uri + 'atoms/' + str(handle))
        get_result = json.loads(get_response.data)['result']['atoms'][0]

        delete_response = \
            self.client.delete(self.uri + 'atoms/' + str(atom.h.value()))
        delete_result = json.loads(delete_response.data)['result']

        assert delete_result['success']
        assert delete_result['handle'] == get_result['handle']

        # Confirm the atom isn't contained in the AtomSpace anymore
        assert_raises(self.atomspace[atom.h], IndexError)

    @raises(IndexError)
    def test_delete_link(self):
        atom = self.bird_animal
        handle = atom.h.value()
        get_response = self.client.get(self.uri + 'atoms/' + str(handle))
        get_result = json.loads(get_response.data)['result']['atoms'][0]

        delete_response = \
            self.client.delete(self.uri + 'atoms/' + str(atom.h.value()))
        delete_result = json.loads(delete_response.data)['result']

        assert delete_result['success']
        assert delete_result['handle'] == get_result['handle']

        # Confirm the atom isn't contained in the AtomSpace anymore
        assert_raises(self.atomspace[atom.h], IndexError)

    def test_get_atoms_by_av(self):
        # Assign some STI values
        self.atomspace.set_av(h=self.bird.h, sti=9)
        self.atomspace.set_av(h=self.swan.h, sti=20)
        self.atomspace.set_av(h=self.bird_animal.h, sti=15)
        self.atomspace.set_av(h=self.animal.h, sti=0)

        get_response = \
            self.client.get(self.uri + 'atoms?filterby=attentionalfocus')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 3
        assert set([atom['handle'] for atom in get_result]) \
            == {self.bird.h.value(),
                self.swan.h.value(),
                self.bird_animal.h.value()}

        get_response = \
            self.client.get(self.uri + 'atoms?filterby=stirange&stimin=15')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 2
        assert set([atom['handle'] for atom in get_result]) \
            == {self.swan.h.value(), self.bird_animal.h.value()}

        get_response = self.client.get(
            self.uri + 'atoms?filterby=stirange&stimin=15&stimax=18')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 1
        assert set([atom['handle'] for atom in get_result]) \
            == {self.bird_animal.h.value()}

    def test_get_types(self):
        # Verify that a list of valid atom types was returned
        get_response = self.client.get(self.uri + 'types')
        get_result = json.loads(get_response.data)['types']
        assert len(get_result) > 0
        assert get_result.__contains__('ConceptNode')

    def test_tv_filter(self):
        # Should return animal, swan_bird, bird_animal (3 atoms)
        get_response = self.client.get(self.uri + 'atoms?tvStrengthMin=0.1')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 3

        # Should return frog (1 atom)
        get_response = self.client.get(self.uri + 'atoms?tvConfidenceMin=0.7')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 1

        # Should return frog (1 atom)
        get_response = self.client.get(self.uri + 'atoms?tvCountMin=2000')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 1

    def test_include_incoming_outgoing(self):
        # Should return bird and swan (2 atoms)
        get_response = self.client.get(
            self.uri +
            'atoms?filterby=stirange&stimin=1&includeIncoming=false')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 2

        # Should additionally return swan_bird and bird_animal (4 atoms)
        get_response = self.client.get(
            self.uri + 'atoms?filterby=stirange&stimin=1&includeIncoming=true')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 4

        # Should additionally return animal (5 atoms)
        get_response = \
            self.client.get(
                self.uri + 'atoms?filterby=stirange&stimin=1&'
                           'includeIncoming=true&includeOutgoing=true')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 5

    def test_scheme_command(self):
        # Test an arbitrary Scheme command to ensure the binding is working
        # properly

        # Set the AttentionalFocus Boundary
        command = {'command': '(cog-set-af-boundary! 5)'}

        post_response = self.client.post(
            self.uri + 'scheme',
            data=json.dumps(command),
            headers=self.headers)
        post_result = json.loads(post_response.data)['response']

        # Verify that the value returned by the POST request is the new
        # value of the AttentionalFocus Boundary
        assert post_result == "5\n"

        # Get the AttentionalFocus Boundary
        command = {'command': '(cog-af-boundary)'}

        post_response = self.client.post(
            self.uri + 'scheme',
            data=json.dumps(command),
            headers=self.headers)
        post_result = json.loads(post_response.data)['response']

        # Verify that it matches the previous response
        assert post_result == "5\n"
Beispiel #4
0
class TestRESTApi():
    """
    Unit tests for the OpenCog REST API.

    See: opencog/python/web/api/apimain.py for the class definitions
    Documentation: http://wiki.opencog.org/w/REST_API
    """
    def setUp(self):
        self.uri = '/api/v1.1/'
        self.headers = {'content-type': 'application/json'}

        # Populate a test AtomSpace
        self.atomspace = AtomSpace()
        self.animal = self.atomspace.add_node(types.ConceptNode, 'animal',
                                              TruthValue(.1, .95))
        self.bird = self.atomspace.add_node(types.ConceptNode, 'bird',
                                            TruthValue(.01, .9))
        self.swan = self.atomspace.add_node(types.ConceptNode, 'swan',
                                            TruthValue(.001, .9))
        self.frog = self.atomspace.add_node(types.ConceptNode, 'frog',
                                            TruthValue(.001, 2000))
        self.swan_bird = self.atomspace.add_link(types.InheritanceLink,
                                                 [self.swan, self.bird],
                                                 TruthValue(1, .9))
        self.bird_animal = self.atomspace.add_link(types.InheritanceLink,
                                                   [self.bird, self.animal],
                                                   TruthValue(1, .9))
        self.bird.av = {'sti': 9}
        self.swan.av = {'sti': 9}

        self.api = RESTAPI(self.atomspace)
        self.client = self.api.test()

    def tearDown(self):
        del self.api
        del self.client

    def test_post_and_get_node(self):
        # Create a test node
        truthvalue = {
            'type': 'simple',
            'details': {
                'strength': 0.08,
                'count': 0.2
            }
        }
        atom = {
            'type': 'ConceptNode',
            'name': 'giant_frog',
            'truthvalue': truthvalue
        }

        post_response = self.client.post(self.uri + 'atoms',
                                         data=json.dumps(atom),
                                         headers=self.headers)
        post_result = json.loads(post_response.data)['atoms']

        # Verify values returned by the POST request
        assert post_result['type'] == atom['type']
        assert post_result['name'] == atom['name']
        assert post_result['truthvalue']['type'] == truthvalue['type']
        assert_almost_equals(float(
            post_result['truthvalue']['details']['strength']),
                             truthvalue['details']['strength'],
                             places=5)
        assert_almost_equals(float(
            post_result['truthvalue']['details']['count']),
                             truthvalue['details']['count'],
                             places=5)

        # Compare to the values created in the AtomSpace
        atomspace_result = self.atomspace[Handle(post_result['handle'])]
        assert Handle(post_result['handle']) == atomspace_result.h
        assert post_result['name'] == atomspace_result.name
        assert types.__dict__.get(post_result['type']) == atomspace_result.type
        assert TruthValue(
            float(post_result['truthvalue']['details']['strength']),
            float(post_result['truthvalue']['details']
                  ['count'])) == atomspace_result.tv

        # Get by handle and compare
        handle = post_result['handle']
        get_response_handle = self.client.get(self.uri + 'atoms/' +
                                              str(handle))
        get_result_handle = json.loads(
            get_response_handle.data)['result']['atoms'][0]
        assert post_result == get_result_handle

        # Get by name and compare
        name = post_result['name']
        get_response_name = self.client.get(self.uri + 'atoms?name=' + name)
        get_result_name = json.loads(
            get_response_name.data)['result']['atoms'][0]
        assert post_result == get_result_name

        # Get by name and type and compare
        type = post_result['type']
        get_response_name_type = self.client.get(self.uri + 'atoms?name=' +
                                                 name + '&type=' + type)
        get_result_name_type = json.loads(
            get_response_name_type.data)['result']['atoms'][0]
        assert post_result == get_result_name_type

    def test_post_and_get_link(self):
        # Create a test link between swan and animal
        truthvalue = {
            'type': 'simple',
            'details': {
                'strength': 0.5,
                'count': 0.4
            }
        }
        atom = {
            'type': 'InheritanceLink',
            'truthvalue': truthvalue,
            'outgoing': [self.swan.h.value(),
                         self.animal.h.value()]
        }

        post_response = self.client.post(self.uri + 'atoms',
                                         data=json.dumps(atom),
                                         headers=self.headers)
        post_result = json.loads(post_response.data)['atoms']

        # Verify values returned by the POST request
        assert post_result['type'] == atom['type']
        assert post_result['truthvalue']['type'] == truthvalue['type']
        assert_almost_equals(float(
            post_result['truthvalue']['details']['strength']),
                             truthvalue['details']['strength'],
                             places=5)
        assert_almost_equals(float(
            post_result['truthvalue']['details']['count']),
                             truthvalue['details']['count'],
                             places=5)
        assert self.swan.h.value() in post_result['outgoing']
        assert self.animal.h.value() in post_result['outgoing']

        # Compare to the values created in the AtomSpace
        atomspace_result = self.atomspace[Handle(post_result['handle'])]
        assert Handle(post_result['handle']) == atomspace_result.h
        assert types.__dict__.get(post_result['type']) == atomspace_result.type
        assert TruthValue(
            float(post_result['truthvalue']['details']['strength']),
            float(post_result['truthvalue']['details']
                  ['count'])) == atomspace_result.tv

        # Get by handle and compare
        handle = post_result['handle']
        get_response_handle = self.client.get(self.uri + 'atoms/' +
                                              str(handle))
        get_result_handle = json.loads(
            get_response_handle.data)['result']['atoms'][0]
        assert post_result == get_result_handle

        # Check if the link is in the incoming set of each of the nodes
        for h in post_result['outgoing']:
            assert Handle(post_result['handle']) in [
                atom.h for atom in self.atomspace[Handle(h)].incoming
            ]

    def test_put_and_get_tv_av_node(self):
        atom = self.swan
        truthvalue = {
            'type': 'simple',
            'details': {
                'strength': 0.005,
                'count': 0.8
            }
        }
        attentionvalue = {'sti': 9, 'lti': 2, 'vlti': True}
        atom_update = {
            'truthvalue': truthvalue,
            'attentionvalue': attentionvalue
        }
        put_response = self.client.put(self.uri + 'atoms/' +
                                       str(atom.h.value()),
                                       data=json.dumps(atom_update),
                                       headers=self.headers)
        put_result = json.loads(put_response.data)['atoms']

        # Verify values returned by the PUT request
        assert put_result['handle'] == atom.h.value()
        assert_almost_equals(float(
            put_result['truthvalue']['details']['strength']),
                             truthvalue['details']['strength'],
                             places=5)
        assert_almost_equals(float(
            put_result['truthvalue']['details']['count']),
                             truthvalue['details']['count'],
                             places=5)
        assert put_result['attentionvalue']['sti'] == attentionvalue['sti']
        assert put_result['attentionvalue']['lti'] == attentionvalue['lti']
        assert put_result['attentionvalue']['vlti'] == attentionvalue['vlti']

        # Compare to the values updated in the AtomSpace
        atomspace_result = self.atomspace[Handle(put_result['handle'])]
        assert Handle(put_result['handle']) == atomspace_result.h
        assert types.__dict__.get(put_result['type']) == atomspace_result.type
        assert TruthValue(
            float(put_result['truthvalue']['details']['strength']),
            float(put_result['truthvalue']['details']
                  ['count'])) == atomspace_result.tv
        assert put_result['attentionvalue'] == atomspace_result.av

        # Get by handle and compare
        get_response = self.client.get(self.uri + 'atoms/' +
                                       str(atom.h.value()))
        get_result = json.loads(get_response.data)['result']['atoms'][0]
        assert put_result == get_result

    def test_put_and_get_tv_av_link(self):
        atom = self.bird_animal
        truthvalue = {
            'type': 'simple',
            'details': {
                'strength': 0.9,
                'count': 0.95
            }
        }
        attentionvalue = {'sti': 6, 'lti': 3, 'vlti': True}
        atom_update = {
            'truthvalue': truthvalue,
            'attentionvalue': attentionvalue
        }
        put_response =\
            self.client.put(self.uri + 'atoms/' + str(atom.h.value()), data=json.dumps(atom_update), headers=self.headers)
        put_result = json.loads(put_response.data)['atoms']

        # Verify values returned by the PUT request
        assert put_result['handle'] == atom.h.value()
        assert_almost_equals(float(
            put_result['truthvalue']['details']['strength']),
                             truthvalue['details']['strength'],
                             places=5)
        assert_almost_equals(float(
            put_result['truthvalue']['details']['count']),
                             truthvalue['details']['count'],
                             places=5)
        assert put_result['attentionvalue']['sti'] == attentionvalue['sti']
        assert put_result['attentionvalue']['lti'] == attentionvalue['lti']
        assert put_result['attentionvalue']['vlti'] == attentionvalue['vlti']

        # Compare to the values updated in the AtomSpace
        atomspace_result = self.atomspace[Handle(put_result['handle'])]
        assert Handle(put_result['handle']) == atomspace_result.h
        assert types.__dict__.get(put_result['type']) == atomspace_result.type
        assert TruthValue(
            float(put_result['truthvalue']['details']['strength']),
            float(put_result['truthvalue']['details']
                  ['count'])) == atomspace_result.tv
        assert put_result['attentionvalue'] == atomspace_result.av

        # Get by handle and compare
        get_response = self.client.get(self.uri + 'atoms/' +
                                       str(atom.h.value()))
        get_result = json.loads(get_response.data)['result']['atoms'][0]
        assert put_result == get_result

    def test_post_revise_existing_node(self):
        # Attempt to create a node, where a node already exists with that name and type. Should revise existing node.
        existing_atom = self.bird

        truthvalue = {
            'type': 'simple',
            'details': {
                'strength': 0.1,
                'count': 0.95
            }
        }
        atom = {
            'type': 'ConceptNode',
            'name': 'bird',
            'truthvalue': truthvalue
        }

        post_response = self.client.post(self.uri + 'atoms',
                                         data=json.dumps(atom),
                                         headers=self.headers)
        post_result = json.loads(post_response.data)['atoms']

        # Verify values returned by the POST request
        assert_almost_equals(float(
            post_result['truthvalue']['details']['strength']),
                             truthvalue['details']['strength'],
                             places=5)
        assert_almost_equals(float(
            post_result['truthvalue']['details']['count']),
                             truthvalue['details']['count'],
                             places=5)

        # Compare to the values updated in the AtomSpace
        assert post_result['handle'] == existing_atom.h.value()
        assert TruthValue(
            float(post_result['truthvalue']['details']['strength']),
            float(post_result['truthvalue']['details']
                  ['count'])) == existing_atom.tv

    def test_post_revise_existing_link(self):
        # Attempt to create a link, where a link already exists with that name and outgoing set.
        # Should revise existing link.
        existing_atom = self.bird_animal
        truthvalue = {
            'type': 'simple',
            'details': {
                'strength': 0.1,
                'count': 0.95
            }
        }
        outgoing = [a.h.value() for a in existing_atom.out]
        atom = {
            'type': 'InheritanceLink',
            'truthvalue': truthvalue,
            'outgoing': outgoing
        }

        post_response = self.client.post(self.uri + 'atoms',
                                         data=json.dumps(atom),
                                         headers=self.headers)
        post_result = json.loads(post_response.data)['atoms']

        # Verify values returned by the POST request
        assert post_result['outgoing'] == outgoing
        assert_almost_equals(float(
            post_result['truthvalue']['details']['strength']),
                             truthvalue['details']['strength'],
                             places=5)
        assert_almost_equals(float(
            post_result['truthvalue']['details']['count']),
                             truthvalue['details']['count'],
                             places=5)

        # Compare to the values updated in the AtomSpace
        assert post_result['handle'] == existing_atom.h.value()
        assert TruthValue(
            float(post_result['truthvalue']['details']['strength']),
            float(post_result['truthvalue']['details']
                  ['count'])) == existing_atom.tv

    @raises(IndexError)
    def test_delete_node(self):
        atom = self.swan
        handle = atom.h.value()
        get_response = self.client.get(self.uri + 'atoms/' + str(handle))
        get_result = json.loads(get_response.data)['result']['atoms'][0]

        delete_response = self.client.delete(self.uri + 'atoms/' +
                                             str(atom.h.value()))
        delete_result = json.loads(delete_response.data)['result']

        assert delete_result['success']
        assert delete_result['handle'] == get_result['handle']

        # Confirm the atom isn't contained in the AtomSpace anymore
        assert_raises(self.atomspace[atom.h], IndexError)

    @raises(IndexError)
    def test_delete_link(self):
        atom = self.bird_animal
        handle = atom.h.value()
        get_response = self.client.get(self.uri + 'atoms/' + str(handle))
        get_result = json.loads(get_response.data)['result']['atoms'][0]

        delete_response = self.client.delete(self.uri + 'atoms/' +
                                             str(atom.h.value()))
        delete_result = json.loads(delete_response.data)['result']

        assert delete_result['success']
        assert delete_result['handle'] == get_result['handle']

        # Confirm the atom isn't contained in the AtomSpace anymore
        assert_raises(self.atomspace[atom.h], IndexError)

    def test_get_atoms_by_av(self):
        # Assign some STI values
        self.atomspace.set_av(h=self.bird.h, sti=9)
        self.atomspace.set_av(h=self.swan.h, sti=20)
        self.atomspace.set_av(h=self.bird_animal.h, sti=15)
        self.atomspace.set_av(h=self.animal.h, sti=0)

        get_response = self.client.get(self.uri +
                                       'atoms?filterby=attentionalfocus')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 3
        assert set([atom['handle'] for atom in get_result]) == set([
            self.bird.h.value(),
            self.swan.h.value(),
            self.bird_animal.h.value()
        ])

        get_response = self.client.get(self.uri +
                                       'atoms?filterby=stirange&stimin=15')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 2
        assert set([atom['handle'] for atom in get_result]) == set(
            [self.swan.h.value(),
             self.bird_animal.h.value()])

        get_response = self.client.get(
            self.uri + 'atoms?filterby=stirange&stimin=15&stimax=18')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 1
        assert set([atom['handle'] for atom in get_result
                    ]) == set([self.bird_animal.h.value()])

    def test_get_types(self):
        # Verify that a list of valid atom types was returned
        get_response = self.client.get(self.uri + 'types')
        get_result = json.loads(get_response.data)['types']
        assert len(get_result) > 0
        assert get_result.__contains__('ConceptNode')

    def test_tv_filter(self):
        # Should return animal, swan_bird, bird_animal (3 atoms)
        get_response = self.client.get(self.uri + 'atoms?tvStrengthMin=0.1')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 3

        # Should return frog (1 atom)
        get_response = self.client.get(self.uri + 'atoms?tvConfidenceMin=0.7')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 1

        # Should return frog (1 atom)
        get_response = self.client.get(self.uri + 'atoms?tvCountMin=2000')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 1

    def test_include_incoming_outgoing(self):
        # Should return bird and swan (2 atoms)
        get_response = self.client.get(
            self.uri +
            'atoms?filterby=stirange&stimin=1&includeIncoming=false')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 2

        # Should additionally return swan_bird and bird_animal (4 atoms)
        get_response = self.client.get(
            self.uri + 'atoms?filterby=stirange&stimin=1&includeIncoming=true')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 4

        # Should additionally return animal (5 atoms)
        get_response = self.client.get(
            self.uri +
            'atoms?filterby=stirange&stimin=1&includeIncoming=true&includeOutgoing=true'
        )
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 5
Beispiel #5
0
class TestRESTApi():
    """
    Unit tests for the OpenCog REST API.

    See: opencog/python/web/api/apimain.py for the class definitions
    Documentation: http://wiki.opencog.org/w/REST_API
    """

    def setUp(self):
        self.uri = '/api/v1.1/'
        self.headers = {'content-type': 'application/json'}

        # Populate a test AtomSpace
        self.atomspace = AtomSpace()
        self.animal = self.atomspace.add_node(
            types.ConceptNode, 'animal', TruthValue(.1, 0.0011860914528369904))
        self.bird = self.atomspace.add_node(
            types.ConceptNode, 'bird', TruthValue(.01, 0.0011237357975915074))
        self.swan = self.atomspace.add_node(
            types.ConceptNode, 'swan', TruthValue(.001, 0.0011237357975915074))
        self.frog = self.atomspace.add_node(
            types.ConceptNode, 'frog', TruthValue(.001, 0.7142857313156128))
        self.swan_bird = self.atomspace.add_link(
            types.InheritanceLink, [self.swan, self.bird], TruthValue(1, 0.0011237357975915074))
        self.bird_animal = self.atomspace.add_link(
            types.InheritanceLink, [self.bird, self.animal], TruthValue(1, 0.0011237357975915074))
        self.bird.sti = 9
        self.swan.sti = 9

        self.api = RESTAPI(self.atomspace)
        self.client = self.api.test()

    def tearDown(self):
        del self.api
        del self.client


    def mkatom(self, json_atom):
        post_response = self.client.post(self.uri + 'atoms',
                                         data=json.dumps(json_atom),
                                         headers=self.headers)
        post_result = json.loads(post_response.data)['atoms']
        return post_result

    def mkswan(self):
        return self.mkatom(
                {'type': 'ConceptNode', 'name': 'swan',
                'truthvalue': {'type': 'simple',
                'details': {'strength': 0.001, 'count': 0.9}}})

    def mkbird(self):
        return self.mkatom(
                {'type': 'ConceptNode', 'name': 'bird',
                'truthvalue': {'type': 'simple',
                'details': {'strength': 0.01, 'count': 0.9}}})

    def mkanimal(self):
        return self.mkatom(
                {'type': 'ConceptNode', 'name': 'animal',
                'truthvalue': {'type': 'simple',
                'details': {'strength': 0.1, 'count': 0.95}}})

    def mkbird_animal(self):
        jbird = self.mkbird()
        janimal = self.mkanimal()
        return self.mkatom(
            {'type': 'InheritanceLink', 'truthvalue':
            {'type': 'simple', 'details': {'strength': 1.0, 'count': 0.9}},
             'outgoing': [jbird['handle'], janimal['handle']]})

    def get_atom(self, handle):
        get_response_handle = \
            self.client.get(self.uri + 'atoms/' + str(handle))
        get_result_handle = \
            json.loads(get_response_handle.data)['result']['atoms'][0]
        return get_result_handle

    def test_a_post_and_get_node(self):
        # Create a test node
        truthvalue = {'type': 'simple',
                      'details': {'strength': 0.08, 'count': 0.2}}
        jatom = {'type': 'ConceptNode',
                'name': 'giant_frog',
                'truthvalue': truthvalue}

        post_response = self.client.post(self.uri + 'atoms',
                                         data=json.dumps(jatom),
                                         headers=self.headers)
        post_result = json.loads(post_response.data)['atoms']

        # Verify values returned by the POST request
        assert post_result['type'] == jatom['type']
        assert post_result['name'] == jatom['name']
        assert post_result['truthvalue']['type'] == truthvalue['type']
        assert_almost_equals(
            float(post_result['truthvalue']['details']['strength']),
            truthvalue['details']['strength'], places=5)
        assert_almost_equals(
            float(post_result['truthvalue']['details']['count']),
            truthvalue['details']['count'], places=5)

        # Compare to the values created in the AtomSpace
        frog = self.atomspace.add_node(
            types.ConceptNode, 'giant_frog',
            TruthValue(.08, count_to_confidence(0.2)))
        atomspace_result = frog
        assert post_result['name'] == atomspace_result.name
        assert types.__dict__.get(post_result['type']) == atomspace_result.type
        assert TruthValue(
            float(post_result['truthvalue']['details']['strength']),
            count_to_confidence(
                float(post_result['truthvalue']['details']['count']))) \
            == atomspace_result.tv

        # Get by handle and compare
        handle = post_result['handle']
        get_response_handle = \
            self.client.get(self.uri + 'atoms/' + str(handle))
        get_result_handle = \
            json.loads(get_response_handle.data)['result']['atoms'][0]
        assert post_result == get_result_handle

        # Get by name and compare
        name = post_result['name']
        get_response_name = self.client.get(self.uri + 'atoms?name=' + name)
        get_result_name = \
            json.loads(get_response_name.data)['result']['atoms'][0]
        assert post_result == get_result_name

        # Get by name and type and compare
        type = post_result['type']
        get_response_name_type = \
            self.client.get(self.uri + 'atoms?name=' + name + '&type=' + type)
        get_result_name_type = \
            json.loads(get_response_name_type.data)['result']['atoms'][0]
        assert post_result == get_result_name_type

    def test_b_post_and_get_link(self):
        jswan = self.mkswan()
        janimal = self.mkanimal()

        # Create a test link between swan and animal
        truthvalue = \
            {'type': 'simple', 'details': {'strength': 0.5, 'count': 0.4}}
        atom = {'type': 'InheritanceLink', 'truthvalue': truthvalue,
                'outgoing': [jswan['handle'], janimal['handle']]}

        post_response = self.client.post(
            self.uri + 'atoms', data=json.dumps(atom), headers=self.headers)
        post_result = json.loads(post_response.data)['atoms']

        # Verify values returned by the POST request
        assert post_result['type'] == atom['type']
        assert post_result['truthvalue']['type'] == truthvalue['type']
        assert_almost_equals(
            float(post_result['truthvalue']['details']['strength']),
            truthvalue['details']['strength'], places=5)
        assert_almost_equals(
            float(post_result['truthvalue']['details']['count']),
            truthvalue['details']['count'], places=5)
        assert jswan['handle'] in post_result['outgoing']
        assert janimal['handle'] in post_result['outgoing']

        # Compare to the values created in the AtomSpace
        swan_animal = self.atomspace.add_link(
            types.InheritanceLink, [self.swan, self.animal],
            TruthValue(0.5, count_to_confidence(0.4)))

        atomspace_result = swan_animal
        assert types.__dict__.get(post_result['type']) == atomspace_result.type
        assert TruthValue(
            float(post_result['truthvalue']['details']['strength']),
            count_to_confidence(
                float(post_result['truthvalue']['details']['count']))) \
            == atomspace_result.tv

        # Get by handle and compare
        handle = post_result['handle']
        get_response_handle = \
            self.client.get(self.uri + 'atoms/' + str(handle))
        get_result_handle = \
            json.loads(get_response_handle.data)['result']['atoms'][0]
        assert post_result == get_result_handle

        # Check if the link is in the incoming set of each of the nodes
        jswan = self.get_atom(jswan['handle'])
        janimal = self.get_atom(janimal['handle'])
        for h in post_result['outgoing']:
            assert post_result['handle'] in jswan['incoming']
            assert post_result['handle'] in janimal['incoming']

    def test_c_put_and_get_tv_av_node(self):
        jswan = self.mkswan()

        truthvalue = \
            {'type': 'simple', 'details': {'strength': 0.005, 'count': 0.8}}
        attentionvalue = {'sti': 9, 'lti': 2, 'vlti': True}
        atom_update = \
            {'truthvalue': truthvalue, 'attentionvalue': attentionvalue}
        put_response = self.client.put(
            self.uri + 'atoms/' + str(jswan['handle']),
            data=json.dumps(atom_update),
            headers=self.headers)
        put_result = json.loads(put_response.data)['atoms']

        # Verify values returned by the PUT request
        assert put_result['handle'] == jswan['handle']
        assert_almost_equals(
            float(put_result['truthvalue']['details']['strength']),
            truthvalue['details']['strength'], places=5)
        assert_almost_equals(
            float(put_result['truthvalue']['details']['count']),
            truthvalue['details']['count'], places=5)
        assert put_result['attentionvalue']['sti'] == attentionvalue['sti']
        assert put_result['attentionvalue']['lti'] == attentionvalue['lti']
        assert put_result['attentionvalue']['vlti'] == attentionvalue['vlti']

        # Compare to the values updated in the AtomSpace
        atomspace_result = self.swan
        assert types.__dict__.get(put_result['type']) == atomspace_result.type
        assert TruthValue(
            float(put_result['truthvalue']['details']['strength']),
            count_to_confidence(
                float(put_result['truthvalue']['details']['count']))) \
            == atomspace_result.tv
        assert put_result['attentionvalue'] == atomspace_result.av

        # Get by handle and compare
        get_response = \
            self.client.get(self.uri + 'atoms/' + str(jswan['handle']))
        get_result = json.loads(get_response.data)['result']['atoms'][0]
        assert put_result == get_result

    def test_d_put_and_get_tv_av_link(self):
        jatom = self.mkbird_animal()
        truthvalue = \
            {'type': 'simple', 'details': {'strength': 0.9, 'count': 0.95}}
        attentionvalue = {'sti': 6, 'lti': 3, 'vlti': True}
        atom_update = \
            {'truthvalue': truthvalue, 'attentionvalue': attentionvalue}
        put_response =\
            self.client.put(
                self.uri + 'atoms/' + str(jatom['handle']),
                data=json.dumps(atom_update),
                headers=self.headers)
        put_result = json.loads(put_response.data)['atoms']

        # Verify values returned by the PUT request
        assert put_result['handle'] == jatom['handle']
        assert_almost_equals(
            float(put_result['truthvalue']['details']['strength']),
            truthvalue['details']['strength'], places=5)
        assert_almost_equals(
            float(put_result['truthvalue']['details']['count']),
            truthvalue['details']['count'], places=5)
        assert put_result['attentionvalue']['sti'] == attentionvalue['sti']
        assert put_result['attentionvalue']['lti'] == attentionvalue['lti']
        assert put_result['attentionvalue']['vlti'] == attentionvalue['vlti']

        # Compare to the values updated in the AtomSpace
        atomspace_result = self.bird_animal
        assert types.__dict__.get(put_result['type']) == atomspace_result.type
        assert TruthValue(
            float(put_result['truthvalue']['details']['strength']),
            count_to_confidence(
                float(put_result['truthvalue']['details']['count']))) \
            == atomspace_result.tv
        assert put_result['attentionvalue'] == atomspace_result.av

        # Get by handle and compare
        get_response = \
            self.client.get(self.uri + 'atoms/' + str(jatom['handle']))
        get_result = json.loads(get_response.data)['result']['atoms'][0]
        assert put_result == get_result

    def test_e_post_revise_existing_node(self):
        # Attempt to create a node, where a node already exists with that name
        # and type. Should revise existing node.
        existing_atom = self.bird

        truthvalue = \
            {'type': 'simple', 'details': {'strength': 0.1, 'count': 0.95}}
        atom = \
            {'type': 'ConceptNode', 'name': 'bird', 'truthvalue': truthvalue}

        post_response = self.client.post(self.uri + 'atoms',
                                         data=json.dumps(atom),
                                         headers=self.headers)
        post_result = json.loads(post_response.data)['atoms']

        # Verify values returned by the POST request
        assert_almost_equals(
            float(post_result['truthvalue']['details']['strength']),
            truthvalue['details']['strength'], places=5)
        assert_almost_equals(
            float(post_result['truthvalue']['details']['count']),
            truthvalue['details']['count'], places=5)

        # Compare to the values updated in the AtomSpace
        assert TruthValue(
            float(post_result['truthvalue']['details']['strength']),
            count_to_confidence(
                float(post_result['truthvalue']['details']['count']))) \
               == existing_atom.tv

    def test_f_post_revise_existing_link(self):
        # Attempt to create a link, where a link already exists with that name
        # and outgoing set.
        # Should revise existing link.
        existing_atom = self.bird_animal
        jbird_animal = self.mkbird_animal()
        truthvalue = \
            {'type': 'simple', 'details': {'strength': 0.1, 'count': 0.95}}
        outgoing = jbird_animal['outgoing']
        atom = {'type': 'InheritanceLink',
                'truthvalue': truthvalue,
                'outgoing': outgoing}

        post_response = self.client.post(
            self.uri + 'atoms', data=json.dumps(atom), headers=self.headers)
        post_result = json.loads(post_response.data)['atoms']

        # Verify values returned by the POST request
        assert post_result['outgoing'] == outgoing
        assert_almost_equals(
            float(post_result['truthvalue']['details']['strength']),
            truthvalue['details']['strength'], places=5)
        assert_almost_equals(
            float(post_result['truthvalue']['details']['count']),
            truthvalue['details']['count'], places=5)

        # Compare to the values updated in the AtomSpace
        assert TruthValue(
            float(post_result['truthvalue']['details']['strength']),
            count_to_confidence(
                float(post_result['truthvalue']['details']['count']))) \
               == existing_atom.tv

    # @raises(IndexError)
    def test_g_delete_node(self):
        jswan = self.mkswan()
        handle = jswan['handle']
        get_response = self.client.get(self.uri + 'atoms/' + str(handle))
        get_result = json.loads(get_response.data)['result']['atoms'][0]

        old_size = self.atomspace.size()
        delete_response = \
            self.client.delete(self.uri + 'atoms/' + str(handle))
        delete_result = json.loads(delete_response.data)['result']

        assert delete_result['success']
        assert delete_result['handle'] == get_result['handle']

        # Confirm the atom isn't contained in the AtomSpace anymore
        # Actually, the python bindings don't provide a way to ask
        # this question. So we check the size, instead.  Should shrink
        # by 2, since both the swan, and the inhertance link get
        # deleted.
        new_size = self.atomspace.size()
        assert new_size + 2 == old_size

    # @raises(IndexError)
    def test_h_delete_link(self):
        jatom = self.mkbird_animal()
        handle = jatom['handle']
        get_response = self.client.get(self.uri + 'atoms/' + str(handle))
        get_result = json.loads(get_response.data)['result']['atoms'][0]

        old_size = self.atomspace.size()
        delete_response = \
            self.client.delete(self.uri + 'atoms/' + str(handle))
        delete_result = json.loads(delete_response.data)['result']

        assert delete_result['success']
        assert delete_result['handle'] == get_result['handle']

        # Confirm the atom isn't contained in the AtomSpace anymore
        # Actually, the python bindings don't provide a way to ask
        # this question. So we check the size, instead.  Should shrink
        # by 1, since only the inhertance link gets deleted.
        new_size = self.atomspace.size()
        assert new_size + 1 == old_size

    def test_j_get_types(self):
        # Verify that a list of valid atom types was returned
        get_response = self.client.get(self.uri + 'types')
        get_result = json.loads(get_response.data)['types']
        assert len(get_result) > 0
        assert get_result.__contains__('ConceptNode')

    def test_k_tv_filter(self):
        # Should return animal, swan_bird, bird_animal (3 atoms)
        get_response = self.client.get(self.uri + 'atoms?tvStrengthMin=0.1')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 3

        # Should return frog (1 atom)
        get_response = self.client.get(self.uri + 'atoms?tvConfidenceMin=0.7')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 1

        # Should return frog (1 atom)
        get_response = self.client.get(self.uri + 'atoms?tvCountMin=2000')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 1

    def test_l_include_incoming_outgoing(self):
        # Should return bird and swan (2 atoms)
        get_response = self.client.get(
            self.uri +
            'atoms?filterby=stirange&stimin=1&includeIncoming=false')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 2

        # Should additionally return swan_bird and bird_animal (4 atoms)
        get_response = self.client.get(
            self.uri + 'atoms?filterby=stirange&stimin=1&includeIncoming=true')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 4

        # Should additionally return animal (5 atoms)
        get_response = \
            self.client.get(
                self.uri + 'atoms?filterby=stirange&stimin=1&'
                           'includeIncoming=true&includeOutgoing=true')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 5

    def test_m_scheme_command(self):
        # Test an arbitrary Scheme command to ensure the binding is working
        # properly
        # XXX Emptied because the scheme command (i.e cog-af-boundary) has been removed.
        pass 
    def test_n_dot_export(self):
        # Export the atomspace to DOT format and ensure that there is a
        # properly defined DOT header created and the correct atoms are
        # included in the description

        # TODO: The Python module "graphviz" needs to be added to ocpkg, so
        # that this dependency will be available for the continuous integration
        # system
        try:
            from graph_description import dot

            get_response = self.client.get(
                self.uri +
                'atoms?filterby=attentionalfocus&dot=True')
            get_result = json.loads(get_response.data)['result']
            assert get_result.startswith("// OpenCog Graph")
            assert "digraph" in get_result
            assert "swan" in get_result
            assert "bird" in get_result
            assert get_result.count("label") == 2
        except ImportError:
            pass
Beispiel #6
0
class TestRESTApi():
    """
    Unit tests for the OpenCog REST API.

    See: opencog/python/web/api/apimain.py for the class definitions
    Documentation: http://wiki.opencog.org/w/REST_API
    """
    def setUp(self):
        self.uri = '/api/v1.1/'
        self.headers = {'content-type': 'application/json'}

        # Populate a test AtomSpace
        self.atomspace = AtomSpace()
        self.animal = self.atomspace.add_node(types.ConceptNode, 'animal',
                                              TruthValue(.1, .95))
        self.bird = self.atomspace.add_node(types.ConceptNode, 'bird',
                                            TruthValue(.01, .9))
        self.swan = self.atomspace.add_node(types.ConceptNode, 'swan',
                                            TruthValue(.001, .9))
        self.frog = self.atomspace.add_node(types.ConceptNode, 'frog',
                                            TruthValue(.001, 2000))
        self.swan_bird = self.atomspace.add_link(types.InheritanceLink,
                                                 [self.swan, self.bird],
                                                 TruthValue(1, .9))
        self.bird_animal = self.atomspace.add_link(types.InheritanceLink,
                                                   [self.bird, self.animal],
                                                   TruthValue(1, .9))
        self.bird.av = {'sti': 9}
        self.swan.av = {'sti': 9}

        self.api = RESTAPI(self.atomspace)
        self.client = self.api.test()

    def tearDown(self):
        del self.api
        del self.client

    def test_post_and_get_node(self):
        # Create a test node
        truthvalue = {
            'type': 'simple',
            'details': {
                'strength': 0.08,
                'count': 0.2
            }
        }
        atom = {
            'type': 'ConceptNode',
            'name': 'giant_frog',
            'truthvalue': truthvalue
        }

        post_response = self.client.post(self.uri + 'atoms',
                                         data=json.dumps(atom),
                                         headers=self.headers)
        post_result = json.loads(post_response.data)['atoms']

        # Verify values returned by the POST request
        assert post_result['type'] == atom['type']
        assert post_result['name'] == atom['name']
        assert post_result['truthvalue']['type'] == truthvalue['type']
        assert_almost_equals(float(
            post_result['truthvalue']['details']['strength']),
                             truthvalue['details']['strength'],
                             places=5)
        assert_almost_equals(float(
            post_result['truthvalue']['details']['count']),
                             truthvalue['details']['count'],
                             places=5)

        # Compare to the values created in the AtomSpace
        atomspace_result = self.atomspace[Handle(post_result['handle'])]
        assert Handle(post_result['handle']) == atomspace_result.h
        assert post_result['name'] == atomspace_result.name
        assert types.__dict__.get(post_result['type']) == atomspace_result.type
        assert TruthValue(
            float(post_result['truthvalue']['details']['strength']),
            float(post_result['truthvalue']['details']['count'])) \
            == atomspace_result.tv

        # Get by handle and compare
        handle = post_result['handle']
        get_response_handle = \
            self.client.get(self.uri + 'atoms/' + str(handle))
        get_result_handle = \
            json.loads(get_response_handle.data)['result']['atoms'][0]
        assert post_result == get_result_handle

        # Get by name and compare
        name = post_result['name']
        get_response_name = self.client.get(self.uri + 'atoms?name=' + name)
        get_result_name = \
            json.loads(get_response_name.data)['result']['atoms'][0]
        assert post_result == get_result_name

        # Get by name and type and compare
        type = post_result['type']
        get_response_name_type = \
            self.client.get(self.uri + 'atoms?name=' + name + '&type=' + type)
        get_result_name_type = \
            json.loads(get_response_name_type.data)['result']['atoms'][0]
        assert post_result == get_result_name_type

    def test_post_and_get_link(self):
        # Create a test link between swan and animal
        truthvalue = \
            {'type': 'simple', 'details': {'strength': 0.5, 'count': 0.4}}
        atom = {
            'type': 'InheritanceLink',
            'truthvalue': truthvalue,
            'outgoing': [self.swan.h.value(),
                         self.animal.h.value()]
        }

        post_response = self.client.post(self.uri + 'atoms',
                                         data=json.dumps(atom),
                                         headers=self.headers)
        post_result = json.loads(post_response.data)['atoms']

        # Verify values returned by the POST request
        assert post_result['type'] == atom['type']
        assert post_result['truthvalue']['type'] == truthvalue['type']
        assert_almost_equals(float(
            post_result['truthvalue']['details']['strength']),
                             truthvalue['details']['strength'],
                             places=5)
        assert_almost_equals(float(
            post_result['truthvalue']['details']['count']),
                             truthvalue['details']['count'],
                             places=5)
        assert self.swan.h.value() in post_result['outgoing']
        assert self.animal.h.value() in post_result['outgoing']

        # Compare to the values created in the AtomSpace
        atomspace_result = self.atomspace[Handle(post_result['handle'])]
        assert Handle(post_result['handle']) == atomspace_result.h
        assert types.__dict__.get(post_result['type']) == atomspace_result.type
        assert TruthValue(
            float(post_result['truthvalue']['details']['strength']),
            float(post_result['truthvalue']['details']['count'])) \
            == atomspace_result.tv

        # Get by handle and compare
        handle = post_result['handle']
        get_response_handle = \
            self.client.get(self.uri + 'atoms/' + str(handle))
        get_result_handle = \
            json.loads(get_response_handle.data)['result']['atoms'][0]
        assert post_result == get_result_handle

        # Check if the link is in the incoming set of each of the nodes
        for h in post_result['outgoing']:
            assert Handle(post_result['handle']) \
                in [atom.h for atom in self.atomspace[Handle(h)].incoming]

    def test_put_and_get_tv_av_node(self):
        atom = self.swan
        truthvalue = \
            {'type': 'simple', 'details': {'strength': 0.005, 'count': 0.8}}
        attentionvalue = {'sti': 9, 'lti': 2, 'vlti': True}
        atom_update = \
            {'truthvalue': truthvalue, 'attentionvalue': attentionvalue}
        put_response = self.client.put(self.uri + 'atoms/' +
                                       str(atom.h.value()),
                                       data=json.dumps(atom_update),
                                       headers=self.headers)
        put_result = json.loads(put_response.data)['atoms']

        # Verify values returned by the PUT request
        assert put_result['handle'] == atom.h.value()
        assert_almost_equals(float(
            put_result['truthvalue']['details']['strength']),
                             truthvalue['details']['strength'],
                             places=5)
        assert_almost_equals(float(
            put_result['truthvalue']['details']['count']),
                             truthvalue['details']['count'],
                             places=5)
        assert put_result['attentionvalue']['sti'] == attentionvalue['sti']
        assert put_result['attentionvalue']['lti'] == attentionvalue['lti']
        assert put_result['attentionvalue']['vlti'] == attentionvalue['vlti']

        # Compare to the values updated in the AtomSpace
        atomspace_result = self.atomspace[Handle(put_result['handle'])]
        assert Handle(put_result['handle']) == atomspace_result.h
        assert types.__dict__.get(put_result['type']) == atomspace_result.type
        assert TruthValue(
            float(put_result['truthvalue']['details']['strength']),
            float(put_result['truthvalue']['details']['count'])) \
            == atomspace_result.tv
        assert put_result['attentionvalue'] == atomspace_result.av

        # Get by handle and compare
        get_response = \
            self.client.get(self.uri + 'atoms/' + str(atom.h.value()))
        get_result = json.loads(get_response.data)['result']['atoms'][0]
        assert put_result == get_result

    def test_put_and_get_tv_av_link(self):
        atom = self.bird_animal
        truthvalue = \
            {'type': 'simple', 'details': {'strength': 0.9, 'count': 0.95}}
        attentionvalue = {'sti': 6, 'lti': 3, 'vlti': True}
        atom_update = \
            {'truthvalue': truthvalue, 'attentionvalue': attentionvalue}
        put_response =\
            self.client.put(
                self.uri + 'atoms/' + str(atom.h.value()),
                data=json.dumps(atom_update),
                headers=self.headers)
        put_result = json.loads(put_response.data)['atoms']

        # Verify values returned by the PUT request
        assert put_result['handle'] == atom.h.value()
        assert_almost_equals(float(
            put_result['truthvalue']['details']['strength']),
                             truthvalue['details']['strength'],
                             places=5)
        assert_almost_equals(float(
            put_result['truthvalue']['details']['count']),
                             truthvalue['details']['count'],
                             places=5)
        assert put_result['attentionvalue']['sti'] == attentionvalue['sti']
        assert put_result['attentionvalue']['lti'] == attentionvalue['lti']
        assert put_result['attentionvalue']['vlti'] == attentionvalue['vlti']

        # Compare to the values updated in the AtomSpace
        atomspace_result = self.atomspace[Handle(put_result['handle'])]
        assert Handle(put_result['handle']) == atomspace_result.h
        assert types.__dict__.get(put_result['type']) == atomspace_result.type
        assert TruthValue(
            float(put_result['truthvalue']['details']['strength']),
            float(put_result['truthvalue']['details']['count'])) \
            == atomspace_result.tv
        assert put_result['attentionvalue'] == atomspace_result.av

        # Get by handle and compare
        get_response = \
            self.client.get(self.uri + 'atoms/' + str(atom.h.value()))
        get_result = json.loads(get_response.data)['result']['atoms'][0]
        assert put_result == get_result

    def test_post_revise_existing_node(self):
        # Attempt to create a node, where a node already exists with that name
        # and type. Should revise existing node.
        existing_atom = self.bird

        truthvalue = \
            {'type': 'simple', 'details': {'strength': 0.1, 'count': 0.95}}
        atom = \
            {'type': 'ConceptNode', 'name': 'bird', 'truthvalue': truthvalue}

        post_response = self.client.post(self.uri + 'atoms',
                                         data=json.dumps(atom),
                                         headers=self.headers)
        post_result = json.loads(post_response.data)['atoms']

        # Verify values returned by the POST request
        assert_almost_equals(float(
            post_result['truthvalue']['details']['strength']),
                             truthvalue['details']['strength'],
                             places=5)
        assert_almost_equals(float(
            post_result['truthvalue']['details']['count']),
                             truthvalue['details']['count'],
                             places=5)

        # Compare to the values updated in the AtomSpace
        assert post_result['handle'] == existing_atom.h.value()
        assert TruthValue(
            float(post_result['truthvalue']['details']['strength']),
            float(post_result['truthvalue']['details']['count'])) \
               == existing_atom.tv

    def test_post_revise_existing_link(self):
        # Attempt to create a link, where a link already exists with that name
        # and outgoing set.
        # Should revise existing link.
        existing_atom = self.bird_animal
        truthvalue = \
            {'type': 'simple', 'details': {'strength': 0.1, 'count': 0.95}}
        outgoing = [a.h.value() for a in existing_atom.out]
        atom = {
            'type': 'InheritanceLink',
            'truthvalue': truthvalue,
            'outgoing': outgoing
        }

        post_response = self.client.post(self.uri + 'atoms',
                                         data=json.dumps(atom),
                                         headers=self.headers)
        post_result = json.loads(post_response.data)['atoms']

        # Verify values returned by the POST request
        assert post_result['outgoing'] == outgoing
        assert_almost_equals(float(
            post_result['truthvalue']['details']['strength']),
                             truthvalue['details']['strength'],
                             places=5)
        assert_almost_equals(float(
            post_result['truthvalue']['details']['count']),
                             truthvalue['details']['count'],
                             places=5)

        # Compare to the values updated in the AtomSpace
        assert post_result['handle'] == existing_atom.h.value()
        assert TruthValue(
            float(post_result['truthvalue']['details']['strength']),
            float(post_result['truthvalue']['details']['count'])) \
               == existing_atom.tv

    @raises(IndexError)
    def test_delete_node(self):
        atom = self.swan
        handle = atom.h.value()
        get_response = self.client.get(self.uri + 'atoms/' + str(handle))
        get_result = json.loads(get_response.data)['result']['atoms'][0]

        delete_response = \
            self.client.delete(self.uri + 'atoms/' + str(atom.h.value()))
        delete_result = json.loads(delete_response.data)['result']

        assert delete_result['success']
        assert delete_result['handle'] == get_result['handle']

        # Confirm the atom isn't contained in the AtomSpace anymore
        assert_raises(self.atomspace[atom.h], IndexError)

    @raises(IndexError)
    def test_delete_link(self):
        atom = self.bird_animal
        handle = atom.h.value()
        get_response = self.client.get(self.uri + 'atoms/' + str(handle))
        get_result = json.loads(get_response.data)['result']['atoms'][0]

        delete_response = \
            self.client.delete(self.uri + 'atoms/' + str(atom.h.value()))
        delete_result = json.loads(delete_response.data)['result']

        assert delete_result['success']
        assert delete_result['handle'] == get_result['handle']

        # Confirm the atom isn't contained in the AtomSpace anymore
        assert_raises(self.atomspace[atom.h], IndexError)

    def test_get_atoms_by_av(self):
        # Assign some STI values
        self.atomspace.set_av(h=self.bird.h, sti=9)
        self.atomspace.set_av(h=self.swan.h, sti=20)
        self.atomspace.set_av(h=self.bird_animal.h, sti=15)
        self.atomspace.set_av(h=self.animal.h, sti=0)

        get_response = \
            self.client.get(self.uri + 'atoms?filterby=attentionalfocus')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 3
        assert set([atom['handle'] for atom in get_result]) \
            == {self.bird.h.value(),
                self.swan.h.value(),
                self.bird_animal.h.value()}

        get_response = \
            self.client.get(self.uri + 'atoms?filterby=stirange&stimin=15')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 2
        assert set([atom['handle'] for atom in get_result]) \
            == {self.swan.h.value(), self.bird_animal.h.value()}

        get_response = self.client.get(
            self.uri + 'atoms?filterby=stirange&stimin=15&stimax=18')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 1
        assert set([atom['handle'] for atom in get_result]) \
            == {self.bird_animal.h.value()}

    def test_get_types(self):
        # Verify that a list of valid atom types was returned
        get_response = self.client.get(self.uri + 'types')
        get_result = json.loads(get_response.data)['types']
        assert len(get_result) > 0
        assert get_result.__contains__('ConceptNode')

    def test_tv_filter(self):
        # Should return animal, swan_bird, bird_animal (3 atoms)
        get_response = self.client.get(self.uri + 'atoms?tvStrengthMin=0.1')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 3

        # Should return frog (1 atom)
        get_response = self.client.get(self.uri + 'atoms?tvConfidenceMin=0.7')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 1

        # Should return frog (1 atom)
        get_response = self.client.get(self.uri + 'atoms?tvCountMin=2000')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 1

    def test_include_incoming_outgoing(self):
        # Should return bird and swan (2 atoms)
        get_response = self.client.get(
            self.uri +
            'atoms?filterby=stirange&stimin=1&includeIncoming=false')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 2

        # Should additionally return swan_bird and bird_animal (4 atoms)
        get_response = self.client.get(
            self.uri + 'atoms?filterby=stirange&stimin=1&includeIncoming=true')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 4

        # Should additionally return animal (5 atoms)
        get_response = \
            self.client.get(
                self.uri + 'atoms?filterby=stirange&stimin=1&'
                           'includeIncoming=true&includeOutgoing=true')
        get_result = json.loads(get_response.data)['result']['atoms']
        assert len(get_result) == 5

    def test_scheme_command(self):
        # Test an arbitrary Scheme command to ensure the binding is working
        # properly

        # Set the AttentionalFocus Boundary
        command = {'command': '(cog-set-af-boundary! 5)'}

        post_response = self.client.post(self.uri + 'scheme',
                                         data=json.dumps(command),
                                         headers=self.headers)
        post_result = json.loads(post_response.data)['response']

        # Verify that the value returned by the POST request is the new
        # value of the AttentionalFocus Boundary
        assert post_result == "5\n"

        # Get the AttentionalFocus Boundary
        command = {'command': '(cog-af-boundary)'}

        post_response = self.client.post(self.uri + 'scheme',
                                         data=json.dumps(command),
                                         headers=self.headers)
        post_result = json.loads(post_response.data)['response']

        # Verify that it matches the previous response
        assert post_result == "5\n"

    def test_dot_export(self):
        # Export the atomspace to DOT format and ensure that there is a
        # properly defined DOT header created and the correct atoms are
        # included in the description

        # TODO: The Python module "graphviz" needs to be added to ocpkg, so
        # that this dependency will be available for the continuous integration
        # system
        try:
            from graph_description import dot

            get_response = self.client.get(
                self.uri + 'atoms?filterby=attentionalfocus&dot=True')
            get_result = json.loads(get_response.data)['result']
            assert get_result.startswith("// OpenCog Graph")
            assert "digraph" in get_result
            assert "swan" in get_result
            assert "bird" in get_result
            assert get_result.count("label") == 2
        except ImportError:
            pass