diff options
author | Matthias Baumgartner <dev@igsor.net> | 2023-03-05 19:25:29 +0100 |
---|---|---|
committer | Matthias Baumgartner <dev@igsor.net> | 2023-03-05 19:25:29 +0100 |
commit | 48b6081d0092e9c5a1b0ad79bdde2e51649bf61a (patch) | |
tree | 634198c34aae3c0306ce30ac7452abd7b53a14e8 /test/graph | |
parent | 91437ba89d35bf482f3d9671bb99ef2fc69f5985 (diff) | |
parent | e4845c627e97a6d125bf33d9e7a4a8d373d7fc4a (diff) | |
download | bsfs-48b6081d0092e9c5a1b0ad79bdde2e51649bf61a.tar.gz bsfs-48b6081d0092e9c5a1b0ad79bdde2e51649bf61a.tar.bz2 bsfs-48b6081d0092e9c5a1b0ad79bdde2e51649bf61a.zip |
Merge branch 'develop'v0.23.03
Diffstat (limited to 'test/graph')
-rw-r--r-- | test/graph/ac/test_base.py | 78 | ||||
-rw-r--r-- | test/graph/ac/test_null.py | 76 | ||||
-rw-r--r-- | test/graph/test_graph.py | 258 | ||||
-rw-r--r-- | test/graph/test_nodes.py | 462 | ||||
-rw-r--r-- | test/graph/test_resolve.py | 199 | ||||
-rw-r--r-- | test/graph/test_result.py | 429 | ||||
-rw-r--r-- | test/graph/test_walk.py | 170 |
7 files changed, 1515 insertions, 157 deletions
diff --git a/test/graph/ac/test_base.py b/test/graph/ac/test_base.py new file mode 100644 index 0000000..addecd4 --- /dev/null +++ b/test/graph/ac/test_base.py @@ -0,0 +1,78 @@ + +# imports +import unittest + +# bsie imports +from bsfs import schema as bsc +from bsfs.namespace import ns +from bsfs.query import ast +from bsfs.triple_store import SparqlStore +from bsfs.utils import URI + +# objects to test +from bsfs.graph.ac.base import AccessControlBase + + +## code ## + +class StubAC(AccessControlBase): + def is_protected_predicate(self, pred): + pass + def create(self, node_type, guids): + pass + def link_from_node(self, node_type, guids): + pass + def link_to_node(self, node_type, guids): + pass + def write_literal(self, node_type, guids): + pass + def createable(self, node_type, guids): + pass + def filter_read(self, node_type, query): + pass + def fetch_read(self, node_type, query): + pass + + +class TestAccessControlBase(unittest.TestCase): + def setUp(self): + self.backend = SparqlStore() + self.user = URI('http://www.example.com/me') + + def test_essentials(self): + ac = StubAC(self.backend, self.user) + # equal construction means equal instance + self.assertEqual(StubAC(self.backend, self.user), StubAC(self.backend, self.user)) + self.assertEqual(hash(StubAC(self.backend, self.user)), hash(StubAC(self.backend, self.user))) + self.assertEqual(ac, StubAC(self.backend, self.user)) + self.assertEqual(hash(ac), hash(StubAC(self.backend, self.user))) + # equivalence respects type + class Foo(): pass + self.assertNotEqual(ac, 1234) + self.assertNotEqual(hash(ac), hash(1234)) + self.assertNotEqual(ac, 'hello') + self.assertNotEqual(hash(ac), hash('hello')) + self.assertNotEqual(ac, Foo()) + self.assertNotEqual(hash(ac), hash(Foo())) + # equivalence respects backend + self.assertNotEqual(ac, StubAC(SparqlStore(), self.user)) + self.assertNotEqual(hash(ac), hash(StubAC(SparqlStore(), self.user))) + # equivalence respects user + self.assertNotEqual(ac, StubAC(self.backend, URI('http://www.example.com/you'))) + self.assertNotEqual(hash(ac), hash(StubAC(self.backend, URI('http://www.example.com/you')))) + # string conversion + self.assertEqual(str(ac), f'StubAC({self.user})') + self.assertEqual(repr(ac), f'StubAC({self.user})') + # string conversion respects user + self.assertEqual(str(StubAC(self.backend, URI('http://www.example.com/you'))), + f'StubAC(http://www.example.com/you)') + self.assertEqual(repr(StubAC(self.backend, URI('http://www.example.com/you'))), + f'StubAC(http://www.example.com/you)') + + +## main ## + +if __name__ == '__main__': + unittest.main() + +## EOF ## diff --git a/test/graph/ac/test_null.py b/test/graph/ac/test_null.py index f39c9be..142bc23 100644 --- a/test/graph/ac/test_null.py +++ b/test/graph/ac/test_null.py @@ -1,15 +1,11 @@ -""" -Part of the bsfs test suite. -A copy of the license is provided with the project. -Author: Matthias Baumgartner, 2022 -""" # imports import unittest # bsie imports -from bsfs import schema as _schema +from bsfs import schema as bsc from bsfs.namespace import ns +from bsfs.query import ast from bsfs.triple_store import SparqlStore from bsfs.utils import URI @@ -19,24 +15,28 @@ from bsfs.graph.ac.null import NullAC ## code ## +ns.bse = ns.bsfs.Entity() + class TestNullAC(unittest.TestCase): def setUp(self): self.backend = SparqlStore() - self.backend.schema = _schema.Schema.from_string(''' + self.backend.schema = bsc.from_string(''' prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> prefix xsd: <http://www.w3.org/2001/XMLSchema#> - prefix bsfs: <http://bsfs.ai/schema/> - prefix bsm: <http://bsfs.ai/schema/Meta#> - prefix bse: <http://bsfs.ai/schema/Entity#> + prefix bsfs: <https://schema.bsfs.io/core/> + prefix bsl: <https://schema.bsfs.io/core/Literal/> + prefix bsn: <https://schema.bsfs.io/core/Node#> + prefix bse: <https://schema.bsfs.io/core/Entity#> bsfs:Entity rdfs:subClassOf bsfs:Node . bsfs:Tag rdfs:subClassOf bsfs:Node . xsd:string rdfs:subClassOf bsfs:Literal . - xsd:integer rdfs:subClassOf bsfs:Literal . + bsl:Number rdfs:subClassOf bsfs:Literal . + xsd:integer rdfs:subClassOf bsl:Number . # predicates mandated by Nodes - bsm:t_created rdfs:subClassOf bsfs:Predicate ; + bsn:t_created rdfs:subClassOf bsfs:Predicate ; rdfs:domain bsfs:Node ; rdfs:range xsd:integer ; bsfs:unique "true"^^xsd:boolean . @@ -62,10 +62,40 @@ class TestNullAC(unittest.TestCase): self.p_author = self.backend.schema.predicate(ns.bse.author) self.p_filesize = self.backend.schema.predicate(ns.bse.filesize) self.p_tag = self.backend.schema.predicate(ns.bse.tag) - self.p_created = self.backend.schema.predicate(ns.bsm.t_created) + self.p_created = self.backend.schema.predicate(ns.bsn.t_created) self.ent_type = self.backend.schema.node(ns.bsfs.Entity) self.ent_ids = {URI('http://www.example.com/me/entity#1234'), URI('http://www.example.com/me/entity#4321')} + def test_essentials(self): + ac = NullAC(self.backend, self.user) + # equal construction means equal instance + self.assertEqual(NullAC(self.backend, self.user), NullAC(self.backend, self.user)) + self.assertEqual(hash(NullAC(self.backend, self.user)), hash(NullAC(self.backend, self.user))) + self.assertEqual(ac, NullAC(self.backend, self.user)) + self.assertEqual(hash(ac), hash(NullAC(self.backend, self.user))) + # equivalence respects type + class Foo(): pass + self.assertNotEqual(ac, 1234) + self.assertNotEqual(hash(ac), hash(1234)) + self.assertNotEqual(ac, 'hello') + self.assertNotEqual(hash(ac), hash('hello')) + self.assertNotEqual(ac, Foo()) + self.assertNotEqual(hash(ac), hash(Foo())) + # equivalence respects backend + self.assertNotEqual(ac, NullAC(SparqlStore(), self.user)) + self.assertNotEqual(hash(ac), hash(NullAC(SparqlStore(), self.user))) + # equivalence respects user + self.assertNotEqual(ac, NullAC(self.backend, URI('http://www.example.com/you'))) + self.assertNotEqual(hash(ac), hash(NullAC(self.backend, URI('http://www.example.com/you')))) + # string conversion + self.assertEqual(str(ac), f'NullAC({self.user})') + self.assertEqual(repr(ac), f'NullAC({self.user})') + # string conversion respects user + self.assertEqual(str(NullAC(self.backend, URI('http://www.example.com/you'))), + f'NullAC(http://www.example.com/you)') + self.assertEqual(repr(NullAC(self.backend, URI('http://www.example.com/you'))), + f'NullAC(http://www.example.com/you)') + def test_is_protected_predicate(self): ac = NullAC(self.backend, self.user) self.assertTrue(ac.is_protected_predicate(self.p_created)) @@ -93,6 +123,26 @@ class TestNullAC(unittest.TestCase): ac = NullAC(self.backend, self.user) self.assertSetEqual(self.ent_ids, ac.createable(self.ent_type, self.ent_ids)) + def test_filter_read(self): + query = ast.filter.Or( + ast.filter.Any(ns.bse.tag, ast.filter.Is('http://example.com/tag#1234')), + ast.filter.Any(ns.bse.tag, ast.filter.Is('http://example.com/tag#4321')), + ast.filter.Any(ns.bse.author, ast.filter.Equals('Me, Myself, and I'))) + ac = NullAC(self.backend, self.user) + # NullAC returns query + self.assertEqual(query, ac.filter_read(self.ent_type, query)) + # query can be none + self.assertIsNone(ac.filter_read(self.ent_type, None)) + + def test_fetch_read(self): + query = ast.fetch.All( + ast.fetch.Fetch(ns.bse.tag, ast.fetch.Value(ns.bse.label, 'tag_label')), + ast.fetch.Node(ns.bse.tag, 'tag_node'), + ast.fetch.Value(ns.bse.iso, 'iso')) + ac = NullAC(self.backend, self.user) + # NullAC returns query + self.assertEqual(query, ac.fetch_read(self.ent_type, query)) + ## main ## diff --git a/test/graph/test_graph.py b/test/graph/test_graph.py index 33cf6aa..167168d 100644 --- a/test/graph/test_graph.py +++ b/test/graph/test_graph.py @@ -1,18 +1,17 @@ -""" -Part of the bsfs test suite. -A copy of the license is provided with the project. -Author: Matthias Baumgartner, 2022 -""" # imports +from functools import reduce +import operator import unittest # bsie imports from bsfs import schema +from bsfs.graph.ac import NullAC +from bsfs.graph.nodes import Nodes from bsfs.namespace import ns +from bsfs.query import ast from bsfs.triple_store import SparqlStore from bsfs.utils import URI, errors -from bsfs.graph.nodes import Nodes # objects to test from bsfs.graph.graph import Graph @@ -20,83 +19,95 @@ from bsfs.graph.graph import Graph ## code ## +ns.bse = ns.bsfs.Entity() + class TestGraph(unittest.TestCase): def setUp(self): - self.user = URI('http://example.com/me') self.backend = SparqlStore.Open() - self.backend.schema = schema.Schema.from_string(''' + self.backend.schema = schema.from_string(''' prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> - prefix bsfs: <http://bsfs.ai/schema/> + prefix bsfs: <https://schema.bsfs.io/core/> bsfs:Entity rdfs:subClassOf bsfs:Node . ''') + self.user = URI('http://example.com/me') + self.ac = NullAC(self.backend, self.user) def test_str(self): - self.assertEqual(str(Graph(self.backend, self.user)), - 'Graph(SparqlStore(uri=None), http://example.com/me)') - self.assertEqual(repr(Graph(self.backend, self.user)), - 'Graph(backend=SparqlStore(uri=None), user=http://example.com/me)') + self.assertEqual(str(Graph(self.backend, self.ac)), + 'Graph(SparqlStore(uri=None))') + self.assertEqual(repr(Graph(self.backend, self.ac)), + 'Graph(SparqlStore(uri=None), NullAC(http://example.com/me))') # str respects backend class Foo(SparqlStore): pass - self.assertEqual(str(Graph(Foo.Open(), self.user)), - 'Graph(Foo(uri=None), http://example.com/me)') - self.assertEqual(repr(Graph(Foo.Open(), self.user)), - 'Graph(backend=Foo(uri=None), user=http://example.com/me)') + self.assertEqual(str(Graph(Foo.Open(), self.ac)), + 'Graph(Foo(uri=None))') + self.assertEqual(repr(Graph(Foo.Open(), self.ac)), + 'Graph(Foo(uri=None), NullAC(http://example.com/me))') # str respect user - self.assertEqual(str(Graph(self.backend, URI('http://example.com/you'))), - 'Graph(SparqlStore(uri=None), http://example.com/you)') - self.assertEqual(repr(Graph(self.backend, URI('http://example.com/you'))), - 'Graph(backend=SparqlStore(uri=None), user=http://example.com/you)') + self.assertEqual(str(Graph(self.backend, NullAC(self.backend, URI('http://example.com/you')))), + 'Graph(SparqlStore(uri=None))') + self.assertEqual(repr(Graph(self.backend, NullAC(self.backend, URI('http://example.com/you')))), + 'Graph(SparqlStore(uri=None), NullAC(http://example.com/you))') # str respects type class Bar(Graph): pass - self.assertEqual(str(Bar(self.backend, self.user)), - 'Bar(SparqlStore(uri=None), http://example.com/me)') - self.assertEqual(repr(Bar(self.backend, self.user)), - 'Bar(backend=SparqlStore(uri=None), user=http://example.com/me)') + self.assertEqual(str(Bar(self.backend, self.ac)), + 'Bar(SparqlStore(uri=None))') + self.assertEqual(repr(Bar(self.backend, self.ac)), + 'Bar(SparqlStore(uri=None), NullAC(http://example.com/me))') def test_equality(self): - graph = Graph(self.backend, self.user) + graph = Graph(self.backend, self.ac) # instance is equal to itself self.assertEqual(graph, graph) self.assertEqual(hash(graph), hash(graph)) # instance is equal to a clone - self.assertEqual(graph, Graph(self.backend, self.user)) - self.assertEqual(hash(graph), hash(Graph(self.backend, self.user))) + self.assertEqual(graph, Graph(self.backend, self.ac)) + self.assertEqual(hash(graph), hash(Graph(self.backend, self.ac))) # equality respects backend - self.assertNotEqual(graph, Graph(SparqlStore.Open(), self.user)) - self.assertNotEqual(hash(graph), hash(Graph(SparqlStore.Open(), self.user))) + self.assertNotEqual(graph, Graph(SparqlStore.Open(), self.ac)) + self.assertNotEqual(hash(graph), hash(Graph(SparqlStore.Open(), self.ac))) # equality respects user self.assertNotEqual(graph, Graph(self.backend, URI('http://example.com/you'))) self.assertNotEqual(hash(graph), hash(Graph(self.backend, URI('http://example.com/you')))) def test_essentials(self): - graph = Graph(self.backend, self.user) + graph = Graph(self.backend, self.ac) # schema self.assertEqual(graph.schema, self.backend.schema) self.assertRaises(AttributeError, setattr, graph, 'schema', None) def test_node(self): - graph = Graph(self.backend, self.user) + graph = Graph(self.backend, self.ac) guid = URI('http://example.com/me/entity#1234') # returns a Nodes instance self.assertEqual( graph.node(ns.bsfs.Entity, guid), - Nodes(self.backend, self.user, graph.schema.node(ns.bsfs.Entity), {guid})) + Nodes(self.backend, self.ac, graph.schema.node(ns.bsfs.Entity), {guid})) # node_type must be in the schema self.assertRaises(KeyError, graph.node, ns.bsfs.Invalid, guid) def test_nodes(self): - graph = Graph(self.backend, self.user) + graph = Graph(self.backend, self.ac) guids = {URI('http://example.com/me/entity#1234'), URI('http://example.com/me/entity#4321')} # returns a Nodes instance self.assertEqual( graph.nodes(ns.bsfs.Entity, guids), - Nodes(self.backend, self.user, graph.schema.node(ns.bsfs.Entity), guids)) + Nodes(self.backend, self.ac, graph.schema.node(ns.bsfs.Entity), guids)) # node_type must be in the schema self.assertRaises(KeyError, graph.nodes, ns.bsfs.Invalid, guids) + def test_empty(self): + graph = Graph(self.backend, self.ac) + # returns a Nodes instance + self.assertEqual( + graph.empty(ns.bsfs.Entity), + Nodes(self.backend, self.ac, graph.schema.node(ns.bsfs.Entity), set())) + # node_type must be in the schema + self.assertRaises(KeyError, graph.empty, ns.bsfs.Invalid) + def test_migrate(self): # setup - graph = Graph(self.backend, self.user) + graph = Graph(self.backend, self.ac) # argument must be a schema class Foo(): pass @@ -117,14 +128,16 @@ class TestGraph(unittest.TestCase): schema.Node(ns.bsfs.Node, None)))}), append=False) # can migrate to compatible schema - target_1 = schema.Schema.from_string(''' + target_1 = schema.from_string(''' prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> prefix xsd: <http://www.w3.org/2001/XMLSchema#> - prefix bsfs: <http://bsfs.ai/schema/> - prefix bse: <http://bsfs.ai/schema/Entity#> + prefix bsfs: <https://schema.bsfs.io/core/> + prefix bse: <https://schema.bsfs.io/core/Entity#> + prefix bsl: <https://schema.bsfs.io/core/Literal/> bsfs:Entity rdfs:subClassOf bsfs:Node . xsd:string rdfs:subClassOf bsfs:Literal . - xsd:integer rdfs:subClassOf bsfs:Literal . + bsl:Number rdfs:subClassOf bsfs:Literal . + xsd:integer rdfs:subClassOf bsl:Number . bse:filename rdfs:subClassOf bsfs:Predicate ; rdfs:domain bsfs:Entity ; @@ -141,27 +154,31 @@ class TestGraph(unittest.TestCase): # new schema is applied self.assertLess(target_1, graph.schema) # graph appends its predicates - self.assertEqual(graph.schema, target_1 + schema.Schema.from_string(''' + self.assertEqual(graph.schema, target_1 + schema.from_string(''' prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> prefix xsd: <http://www.w3.org/2001/XMLSchema#> - prefix bsfs: <http://bsfs.ai/schema/> - prefix bsm: <http://bsfs.ai/schema/Meta#> - xsd:integer rdfs:subClassOf bsfs:Literal . - bsm:t_created rdfs:subClassOf bsfs:Predicate ; + prefix bsfs: <https://schema.bsfs.io/core/> + prefix bsn: <https://schema.bsfs.io/core/Node#> + prefix bsl: <https://schema.bsfs.io/core/Literal/> + bsl:Number rdfs:subClassOf bsfs:Literal . + xsd:float rdfs:subClassOf bsl:Number . + bsn:t_created rdfs:subClassOf bsfs:Predicate ; rdfs:domain bsfs:Node ; - rdfs:range xsd:integer ; + rdfs:range xsd:float ; bsfs:unique "true"^^xsd:boolean . ''')) # can overwrite the current schema - target_2 = schema.Schema.from_string(''' + target_2 = schema.from_string(''' prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> prefix xsd: <http://www.w3.org/2001/XMLSchema#> - prefix bsfs: <http://bsfs.ai/schema/> - prefix bse: <http://bsfs.ai/schema/Entity#> + prefix bsfs: <http://schema.bsfs.io/core/> + prefix bse: <http://schema.bsfs.io/core/Node/Entity#> + prefix bsl: <https://schema.bsfs.io/core/Literal/> bsfs:Entity rdfs:subClassOf bsfs:Node . xsd:string rdfs:subClassOf bsfs:Literal . - xsd:integer rdfs:subClassOf bsfs:Literal . + bsl:Number rdfs:subClassOf bsfs:Literal . + xsd:integer rdfs:subClassOf bsl:Number . bse:filename rdfs:subClassOf bsfs:Predicate ; rdfs:domain bsfs:Entity ; @@ -180,18 +197,147 @@ class TestGraph(unittest.TestCase): # new schema is applied self.assertLess(target_2, graph.schema) # graph appends its predicates - self.assertEqual(graph.schema, target_2 + schema.Schema.from_string(''' + self.assertEqual(graph.schema, target_2 + schema.from_string(''' prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> prefix xsd: <http://www.w3.org/2001/XMLSchema#> - prefix bsfs: <http://bsfs.ai/schema/> - prefix bsm: <http://bsfs.ai/schema/Meta#> - xsd:integer rdfs:subClassOf bsfs:Literal . - bsm:t_created rdfs:subClassOf bsfs:Predicate ; + prefix bsfs: <https://schema.bsfs.io/core/> + prefix bsn: <https://schema.bsfs.io/core/Node#> + prefix bsl: <https://schema.bsfs.io/core/Literal/> + bsl:Number rdfs:subClassOf bsfs:Literal . + xsd:float rdfs:subClassOf bsl:Number . + bsn:t_created rdfs:subClassOf bsfs:Predicate ; rdfs:domain bsfs:Node ; - rdfs:range xsd:integer ; + rdfs:range xsd:float ; bsfs:unique "true"^^xsd:boolean . ''')) + def test_get(self): + # setup + graph = Graph(self.backend, self.ac) + graph.migrate(schema.from_string(''' + prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> + prefix xsd: <http://www.w3.org/2001/XMLSchema#> + prefix bsfs: <https://schema.bsfs.io/core/> + prefix bse: <https://schema.bsfs.io/core/Entity#> + + bsfs:Entity rdfs:subClassOf bsfs:Node . + bsfs:Tag rdfs:subClassOf bsfs:Node . + xsd:string rdfs:subClassOf bsfs:Literal . + + bse:tag rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity ; + rdfs:range bsfs:Tag ; + bsfs:unique "false"^^xsd:boolean . + + bse:comment rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Node ; + rdfs:range xsd:string ; + bsfs:unique "false"^^xsd:boolean . + + ''')) + # add some instances + ents = graph.nodes(ns.bsfs.Entity, {URI('http://example.com/entity#1234'), URI('http://example.com/entity#4321')}) + tags = graph.nodes(ns.bsfs.Tag, {URI('http://example.com/tag#1234'), URI('http://example.com/tag#4321')}) + # add some node links + ents.set(ns.bse.tag, tags) + # add some literals + graph.node(ns.bsfs.Entity, URI('http://example.com/entity#1234')).set(ns.bse.comment, 'hello world') + graph.node(ns.bsfs.Entity, URI('http://example.com/entity#1234')).set(ns.bse.comment, 'foo') + graph.node(ns.bsfs.Entity, URI('http://example.com/entity#1234')).set(ns.bse.comment, 'foobar') + graph.node(ns.bsfs.Tag, URI('http://example.com/tag#1234')).set(ns.bse.comment, 'foo') + graph.node(ns.bsfs.Tag, URI('http://example.com/tag#4321')).set(ns.bse.comment, 'bar') + + # invalid query raises exception + self.assertRaises(errors.ConsistencyError, graph.get, ns.bsfs.Entity, ast.filter.Any(ns.bse.tag, ast.filter.Equals('hello world'))) + + # get returns nodes + self.assertEqual(graph.get(ns.bsfs.Entity, ast.filter.Any(ns.bse.tag, ast.filter.Is(tags))), ents) + self.assertEqual(graph.get(ns.bsfs.Entity, ast.filter.Any(ns.bse.comment, ast.filter.StartsWith('foo'))), + graph.node(ns.bsfs.Entity, URI('http://example.com/entity#1234'))) + self.assertEqual(graph.get(ns.bsfs.Node, ast.filter.Any(ns.bse.comment, ast.filter.StartsWith('foo'))), + graph.nodes(ns.bsfs.Node, {URI('http://example.com/entity#1234'), URI('http://example.com/tag#1234')})) + self.assertEqual(graph.get(ns.bsfs.Entity, ast.filter.Or( + ast.filter.Any(ns.bse.comment, ast.filter.EndsWith('bar')), + ast.filter.Any(ns.bse.tag, ast.filter.All(ns.bse.comment, ast.filter.Equals('bar'))))), + ents) + + # query can be None + self.assertEqual(graph.get(ns.bsfs.Entity, None), ents) + + def test_sorted(self): + # setup + graph = Graph(self.backend, self.ac) + graph.migrate(schema.from_string(''' + prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> + prefix xsd: <http://www.w3.org/2001/XMLSchema#> + prefix bsfs: <https://schema.bsfs.io/core/> + prefix bse: <https://schema.bsfs.io/core/Entity#> + + bsfs:Entity rdfs:subClassOf bsfs:Node . + bsfs:Tag rdfs:subClassOf bsfs:Node . + xsd:string rdfs:subClassOf bsfs:Literal . + + bse:tag rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity ; + rdfs:range bsfs:Tag ; + bsfs:unique "false"^^xsd:boolean . + + bse:comment rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Node ; + rdfs:range xsd:string ; + bsfs:unique "false"^^xsd:boolean . + + ''')) + # add some instances + ents = [ + # default is alphabetical order + graph.node(ns.bsfs.Entity, URI('http://example.com/entity#1234')), + graph.node(ns.bsfs.Entity, URI('http://example.com/entity#4321')), + ] + tags = graph.nodes(ns.bsfs.Tag, {URI('http://example.com/tag#1234'), URI('http://example.com/tag#4321')}) + # add some node links + reduce(operator.add, ents).set(ns.bse.tag, tags) + # add some literals + graph.node(ns.bsfs.Entity, URI('http://example.com/entity#1234')).set(ns.bse.comment, 'hello world') + graph.node(ns.bsfs.Entity, URI('http://example.com/entity#1234')).set(ns.bse.comment, 'foo') + graph.node(ns.bsfs.Entity, URI('http://example.com/entity#1234')).set(ns.bse.comment, 'foobar') + graph.node(ns.bsfs.Tag, URI('http://example.com/tag#1234')).set(ns.bse.comment, 'foo') + graph.node(ns.bsfs.Tag, URI('http://example.com/tag#4321')).set(ns.bse.comment, 'bar') + + # invalid query raises exception + self.assertRaises(errors.ConsistencyError, list, graph.sorted(ns.bsfs.Entity, ast.filter.Any(ns.bse.tag, ast.filter.Equals('hello world')))) + + # get returns nodes + self.assertListEqual(list(graph.sorted(ns.bsfs.Entity, ast.filter.Any(ns.bse.tag, ast.filter.Is(tags)))), ents) + self.assertListEqual(list(graph.sorted(ns.bsfs.Entity, ast.filter.Any(ns.bse.comment, ast.filter.StartsWith('foo')))), + [graph.node(ns.bsfs.Entity, URI('http://example.com/entity#1234'))]) + self.assertListEqual(list(graph.sorted(ns.bsfs.Node, ast.filter.Any(ns.bse.comment, ast.filter.StartsWith('foo')))), [ + graph.node(ns.bsfs.Node, URI('http://example.com/entity#1234')), + graph.node(ns.bsfs.Node, URI('http://example.com/tag#1234')), + ]) + self.assertListEqual(list(graph.sorted(ns.bsfs.Entity, ast.filter.Or( + ast.filter.Any(ns.bse.comment, ast.filter.EndsWith('bar')), + ast.filter.Any(ns.bse.tag, ast.filter.All(ns.bse.comment, ast.filter.Equals('bar')))))), + ents) + + # query can be None + self.assertListEqual(list(graph.sorted(ns.bsfs.Entity, None)), ents) + + + def test_all(self): + graph = Graph(self.backend, self.ac) + # resulting nodes can be empty + self.assertEqual(graph.all(ns.bsfs.Entity), + Nodes(self.backend, self.ac, graph.schema.node(ns.bsfs.Entity), set())) + # resulting nodes contains all nodes of the respective type + guids = {URI('http://example.com/me/entity#1234'), URI('http://example.com/me/entity#4321')} + self.backend.create(graph.schema.node(ns.bsfs.Entity), guids) + self.assertEqual(graph.all(ns.bsfs.Entity), + Nodes(self.backend, self.ac, graph.schema.node(ns.bsfs.Entity), guids)) + # node_type must be in the schema + self.assertRaises(KeyError, graph.all, ns.bsfs.Invalid) + + ## main ## diff --git a/test/graph/test_nodes.py b/test/graph/test_nodes.py index 43e7f6f..afe7522 100644 --- a/test/graph/test_nodes.py +++ b/test/graph/test_nodes.py @@ -1,16 +1,17 @@ -""" -Part of the bsfs test suite. -A copy of the license is provided with the project. -Author: Matthias Baumgartner, 2022 -""" -# imports -import rdflib +# standard imports +from functools import partial +import operator import unittest +# external imports +import rdflib + # bsie imports -from bsfs import schema as _schema -from bsfs.namespace import ns +from bsfs import schema as bsc +from bsfs.graph.ac import NullAC +from bsfs.graph.walk import Walk +from bsfs.namespace import Namespace, ns from bsfs.triple_store.sparql import SparqlStore from bsfs.utils import errors, URI @@ -20,27 +21,32 @@ from bsfs.graph.nodes import Nodes ## code ## +ns.bse = ns.bsfs.Entity() +ns.bst = ns.bsfs.Tag() + class TestNodes(unittest.TestCase): def setUp(self): # initialize backend self.backend = SparqlStore() - self.backend.schema = _schema.Schema.from_string(''' + self.backend.schema = bsc.from_string(''' prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> prefix xsd: <http://www.w3.org/2001/XMLSchema#> - prefix bsfs: <http://bsfs.ai/schema/> - prefix bsm: <http://bsfs.ai/schema/Meta#> - prefix bse: <http://bsfs.ai/schema/Entity#> - prefix bst: <http://bsfs.ai/schema/Tag#> + prefix bsfs: <https://schema.bsfs.io/core/> + prefix bsl: <https://schema.bsfs.io/core/Literal/> + prefix bsn: <https://schema.bsfs.io/core/Node#> + prefix bse: <https://schema.bsfs.io/core/Entity#> + prefix bst: <https://schema.bsfs.io/core/Tag#> bsfs:Entity rdfs:subClassOf bsfs:Node . bsfs:Tag rdfs:subClassOf bsfs:Node . bsfs:User rdfs:subClassOf bsfs:Node . xsd:string rdfs:subClassOf bsfs:Literal . - xsd:integer rdfs:subClassOf bsfs:Literal . + bsl:Number rdfs:subClassOf bsfs:Literal . + xsd:integer rdfs:subClassOf bsl:Number . # predicates mandated by Nodes - bsm:t_created rdfs:subClassOf bsfs:Predicate ; + bsn:t_created rdfs:subClassOf bsfs:Predicate ; rdfs:domain bsfs:Node ; rdfs:range xsd:integer ; bsfs:unique "true"^^xsd:boolean . @@ -66,14 +72,40 @@ class TestNodes(unittest.TestCase): rdfs:range bsfs:User ; bsfs:unique "true"^^xsd:boolean . + bst:label rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Tag ; + rdfs:range xsd:string ; + bsfs:unique "true"^^xsd:boolean . + bst:representative rdfs:subClassOf bsfs:Predicate ; rdfs:domain bsfs:Tag ; rdfs:range bsfs:Entity ; bsfs:unique "true"^^xsd:boolean . ''') + self.schema_triples = { + # schema hierarchy + (rdflib.URIRef(ns.bsfs.Entity), rdflib.RDFS.subClassOf, rdflib.URIRef(ns.bsfs.Node)), + (rdflib.URIRef(ns.bsfs.Tag), rdflib.RDFS.subClassOf, rdflib.URIRef(ns.bsfs.Node)), + (rdflib.URIRef(ns.bsfs.User), rdflib.RDFS.subClassOf, rdflib.URIRef(ns.bsfs.Node)), + (rdflib.URIRef(ns.xsd.string), rdflib.RDFS.subClassOf, rdflib.URIRef(ns.bsfs.Literal)), + (rdflib.URIRef(ns.bsl.Array), rdflib.RDFS.subClassOf, rdflib.URIRef(ns.bsfs.Literal)), + (rdflib.URIRef(ns.bsl.BinaryBlob), rdflib.RDFS.subClassOf, rdflib.URIRef(ns.bsfs.Literal)), + (rdflib.URIRef(ns.bsl.Array.Feature), rdflib.RDFS.subClassOf, rdflib.URIRef(ns.bsl.Array)), + (rdflib.URIRef(ns.bsl.Number), rdflib.RDFS.subClassOf, rdflib.URIRef(ns.bsfs.Literal)), + (rdflib.URIRef(ns.bsl.Time), rdflib.RDFS.subClassOf, rdflib.URIRef(ns.bsfs.Literal)), + (rdflib.URIRef(ns.xsd.integer), rdflib.RDFS.subClassOf, rdflib.URIRef(ns.bsl.Number)), + (rdflib.URIRef(ns.bsn.t_created), rdflib.RDFS.subClassOf, rdflib.URIRef(ns.bsfs.Predicate)), + (rdflib.URIRef(ns.bse.comment), rdflib.RDFS.subClassOf, rdflib.URIRef(ns.bsfs.Predicate)), + (rdflib.URIRef(ns.bse.filesize), rdflib.RDFS.subClassOf, rdflib.URIRef(ns.bsfs.Predicate)), + (rdflib.URIRef(ns.bse.tag), rdflib.RDFS.subClassOf, rdflib.URIRef(ns.bsfs.Predicate)), + (rdflib.URIRef(ns.bse.author), rdflib.RDFS.subClassOf, rdflib.URIRef(ns.bsfs.Predicate)), + (rdflib.URIRef(ns.bst.representative), rdflib.RDFS.subClassOf, rdflib.URIRef(ns.bsfs.Predicate)), + (rdflib.URIRef(ns.bst.label), rdflib.RDFS.subClassOf, rdflib.URIRef(ns.bsfs.Predicate)), + } # Nodes constructor args self.user = URI('http://example.com/me') + self.ac = NullAC(self.backend, self.user) # set args self.tag_type = self.backend.schema.node(ns.bsfs.Tag) self.ent_type = self.backend.schema.node(ns.bsfs.Entity) @@ -81,8 +113,9 @@ class TestNodes(unittest.TestCase): self.p_filesize = self.backend.schema.predicate(ns.bse.filesize) self.p_author = self.backend.schema.predicate(ns.bse.author) self.p_tag = self.backend.schema.predicate(ns.bse.tag) - self.p_representative = self.backend.schema.predicate(URI('http://bsfs.ai/schema/Tag#representative')) - self.t_created = self.backend.schema.predicate(ns.bsm.t_created) + self.p_representative = self.backend.schema.predicate(ns.bst.representative) + self.p_label = self.backend.schema.predicate(ns.bst.label) + self.t_created = self.backend.schema.predicate(ns.bsn.t_created) self.ent_ids = { URI('http://example.com/me/entity#1234'), URI('http://example.com/me/entity#4321'), @@ -92,67 +125,71 @@ class TestNodes(unittest.TestCase): URI('http://example.com/me/tag#4321'), } + def test_construct(self): + self.assertIsInstance(Nodes(self.backend, self.ac, self.ent_type, {'http://example.com/me-and-you'}), Nodes) + self.assertRaises(ValueError, Nodes, self.backend, self.ac, self.ent_type, {'http://example.com/me and you'}) + def test_str(self): # str baseline - nodes = Nodes(self.backend, self.user, self.ent_type, self.ent_ids) - self.assertEqual(str(nodes), f'Nodes({self.ent_type}, {self.ent_ids})') - self.assertEqual(repr(nodes), f'Nodes({self.backend}, {self.user}, {self.ent_type}, {self.ent_ids})') + nodes = Nodes(self.backend, self.ac, self.ent_type, {URI('http://example.com/me/entity#1234')}) + self.assertEqual(str(nodes), f"Nodes({self.ent_type}, {{'http://example.com/me/entity#1234'}})") + self.assertEqual(repr(nodes), f"Nodes({self.backend}, {self.ac}, {self.ent_type}, {{'http://example.com/me/entity#1234'}})") # str respects node_type - nodes = Nodes(self.backend, self.user, self.tag_type, self.tag_ids) - self.assertEqual(str(nodes), f'Nodes({self.tag_type}, {self.tag_ids})') - self.assertEqual(repr(nodes), f'Nodes({self.backend}, {self.user}, {self.tag_type}, {self.tag_ids})') + nodes = Nodes(self.backend, self.ac, self.tag_type, {URI('http://example.com/me/tag#1234')}) + self.assertEqual(str(nodes), f"Nodes({self.tag_type}, {{'http://example.com/me/tag#1234'}})") + self.assertEqual(repr(nodes), f"Nodes({self.backend}, {self.ac}, {self.tag_type}, {{'http://example.com/me/tag#1234'}})") # str respects guids - nodes = Nodes(self.backend, self.user, self.ent_type, {URI('http://example.com/me/entity#foo')}) + nodes = Nodes(self.backend, self.ac, self.ent_type, {URI('http://example.com/me/entity#foo')}) self.assertEqual(str(nodes), f'Nodes({self.ent_type}, {{\'http://example.com/me/entity#foo\'}})') - self.assertEqual(repr(nodes), f'Nodes({self.backend}, {self.user}, {self.ent_type}, {{\'http://example.com/me/entity#foo\'}})') + self.assertEqual(repr(nodes), f'Nodes({self.backend}, {self.ac}, {self.ent_type}, {{\'http://example.com/me/entity#foo\'}})') # repr respects backend class Foo(SparqlStore): pass backend = Foo.Open() backend.schema = self.backend.schema - nodes = Nodes(backend, self.user, self.ent_type, self.ent_ids) - self.assertEqual(repr(nodes), f'Nodes({backend}, {self.user}, {self.ent_type}, {self.ent_ids})') + nodes = Nodes(backend, self.ac, self.ent_type, {URI('http://example.com/me/entity#1234')}) + self.assertEqual(repr(nodes), f"Nodes({backend}, {self.ac}, {self.ent_type}, {{'http://example.com/me/entity#1234'}})") # repr respects user - nodes = Nodes(self.backend, URI('http://example.com/you'), self.ent_type, self.ent_ids) - self.assertEqual(repr(nodes), f'Nodes({self.backend}, http://example.com/you, {self.ent_type}, {self.ent_ids})') + nodes = Nodes(self.backend, NullAC(self.backend, URI('http://example.com/you')), self.ent_type, {URI('http://example.com/me/entity#1234')}) + self.assertEqual(repr(nodes), f"Nodes({self.backend}, NullAC(http://example.com/you), {self.ent_type}, {{'http://example.com/me/entity#1234'}})") def test_equality(self): - nodes = Nodes(self.backend, self.user, self.ent_type, self.ent_ids) + nodes = Nodes(self.backend, self.ac, self.ent_type, self.ent_ids) # instance is equal to itself self.assertEqual(nodes, nodes) self.assertEqual(hash(nodes), hash(nodes)) # instance is equal to a clone - self.assertEqual(nodes, Nodes(self.backend, self.user, self.ent_type, self.ent_ids)) - self.assertEqual(Nodes(self.backend, self.user, self.ent_type, self.ent_ids), nodes) - self.assertEqual(hash(nodes), hash(Nodes(self.backend, self.user, self.ent_type, self.ent_ids))) + self.assertEqual(nodes, Nodes(self.backend, self.ac, self.ent_type, self.ent_ids)) + self.assertEqual(Nodes(self.backend, self.ac, self.ent_type, self.ent_ids), nodes) + self.assertEqual(hash(nodes), hash(Nodes(self.backend, self.ac, self.ent_type, self.ent_ids))) # equality respects backend backend = SparqlStore.Open() backend.schema = self.backend.schema - self.assertNotEqual(nodes, Nodes(backend, self.user, self.ent_type, self.ent_ids)) - self.assertNotEqual(hash(nodes), hash(Nodes(backend, self.user, self.ent_type, self.ent_ids))) + self.assertNotEqual(nodes, Nodes(backend, self.ac, self.ent_type, self.ent_ids)) + self.assertNotEqual(hash(nodes), hash(Nodes(backend, self.ac, self.ent_type, self.ent_ids))) # equality respects user - self.assertNotEqual(nodes, Nodes(self.backend, URI('http://example.com/you'), self.ent_type, self.ent_ids)) - self.assertNotEqual(hash(nodes), hash(Nodes(self.backend, URI('http://example.com/you'), self.ent_type, self.ent_ids))) + self.assertNotEqual(nodes, Nodes(self.backend, NullAC(self.backend, URI('http://example.com/you')), self.ent_type, self.ent_ids)) + self.assertNotEqual(hash(nodes), hash(Nodes(self.backend, NullAC(self.backend, URI('http://example.com/you')), self.ent_type, self.ent_ids))) # equality respects node_type - self.assertNotEqual(nodes, Nodes(self.backend, self.user, self.tag_type, self.ent_ids)) - self.assertNotEqual(hash(nodes), hash(Nodes(self.backend, self.user, self.tag_type, self.ent_ids))) + self.assertNotEqual(nodes, Nodes(self.backend, self.ac, self.tag_type, self.ent_ids)) + self.assertNotEqual(hash(nodes), hash(Nodes(self.backend, self.ac, self.tag_type, self.ent_ids))) # equality respects guids - self.assertNotEqual(nodes, Nodes(self.backend, self.user, self.ent_type, self.tag_ids)) - self.assertNotEqual(hash(nodes), hash(Nodes(self.backend, self.user, self.ent_type, self.tag_ids))) + self.assertNotEqual(nodes, Nodes(self.backend, self.ac, self.ent_type, self.tag_ids)) + self.assertNotEqual(hash(nodes), hash(Nodes(self.backend, self.ac, self.ent_type, self.tag_ids))) def test_properties(self): # node_type self.assertEqual(self.ent_type, Nodes( - self.backend, self.user, self.ent_type, self.ent_ids).node_type) + self.backend, self.ac, self.ent_type, self.ent_ids).node_type) self.assertEqual(self.tag_type, Nodes( - self.backend, self.user, self.tag_type, self.tag_ids).node_type) + self.backend, self.ac, self.tag_type, self.tag_ids).node_type) # guids self.assertSetEqual(self.ent_ids, set(Nodes( - self.backend, self.user, self.ent_type, self.ent_ids).guids)) + self.backend, self.ac, self.ent_type, self.ent_ids).guids)) self.assertSetEqual(self.tag_ids, set(Nodes( - self.backend, self.user, self.tag_type, self.tag_ids).guids)) + self.backend, self.ac, self.tag_type, self.tag_ids).guids)) def test__ensure_nodes(self): - nodes = Nodes(self.backend, self.user, self.ent_type, self.ent_ids) + nodes = Nodes(self.backend, self.ac, self.ent_type, self.ent_ids) # missing nodes are created self.assertSetEqual(self.ent_ids, nodes._ensure_nodes(self.ent_type, self.ent_ids)) @@ -160,10 +197,10 @@ class TestNodes(unittest.TestCase): time_triples = list(self.backend._graph.objects(rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(self.t_created.uri))) t_ent_created = float(time_triples[0]) if len(time_triples) > 0 else 0.0 # check triples - self.assertSetEqual(set(self.backend._graph), { + self.assertSetEqual(set(self.backend._graph), self.schema_triples | { # entity definitions - (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.RDF.type, rdflib.URIRef('http://bsfs.ai/schema/Entity')), - (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.RDF.type, rdflib.URIRef('http://bsfs.ai/schema/Entity')), + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.RDF.type, rdflib.URIRef('https://schema.bsfs.io/core/Entity')), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.RDF.type, rdflib.URIRef('https://schema.bsfs.io/core/Entity')), # bookkeeping (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(self.t_created.uri), rdflib.Literal(t_ent_created, datatype=rdflib.XSD.integer)), (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(self.t_created.uri), rdflib.Literal(t_ent_created, datatype=rdflib.XSD.integer)), @@ -171,10 +208,10 @@ class TestNodes(unittest.TestCase): # existing nodes remain unchanged self.assertSetEqual(self.ent_ids, nodes._ensure_nodes(self.ent_type, self.ent_ids)) - self.assertSetEqual(set(self.backend._graph), { + self.assertSetEqual(set(self.backend._graph), self.schema_triples | { # entity definitions - (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.RDF.type, rdflib.URIRef('http://bsfs.ai/schema/Entity')), - (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.RDF.type, rdflib.URIRef('http://bsfs.ai/schema/Entity')), + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.RDF.type, rdflib.URIRef('https://schema.bsfs.io/core/Entity')), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.RDF.type, rdflib.URIRef('https://schema.bsfs.io/core/Entity')), # bookkeeping (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(self.t_created.uri), rdflib.Literal(t_ent_created, datatype=rdflib.XSD.integer)), (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(self.t_created.uri), rdflib.Literal(t_ent_created, datatype=rdflib.XSD.integer)), @@ -186,23 +223,23 @@ class TestNodes(unittest.TestCase): time_triples = list(self.backend._graph.objects(rdflib.URIRef('http://example.com/me/tag#1234'), rdflib.URIRef(self.t_created.uri))) t_tag_created = float(time_triples[0]) if len(time_triples) > 0 else 0.0 # check triples - self.assertSetEqual(set(self.backend._graph), { + self.assertSetEqual(set(self.backend._graph), self.schema_triples | { # previous triples - (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.RDF.type, rdflib.URIRef('http://bsfs.ai/schema/Entity')), - (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.RDF.type, rdflib.URIRef('http://bsfs.ai/schema/Entity')), + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.RDF.type, rdflib.URIRef('https://schema.bsfs.io/core/Entity')), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.RDF.type, rdflib.URIRef('https://schema.bsfs.io/core/Entity')), (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(self.t_created.uri), rdflib.Literal(t_ent_created, datatype=rdflib.XSD.integer)), (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(self.t_created.uri), rdflib.Literal(t_ent_created, datatype=rdflib.XSD.integer)), # new triples - (rdflib.URIRef('http://example.com/me/tag#1234'), rdflib.RDF.type, rdflib.URIRef('http://bsfs.ai/schema/Tag')), - (rdflib.URIRef('http://example.com/me/tag#4321'), rdflib.RDF.type, rdflib.URIRef('http://bsfs.ai/schema/Tag')), + (rdflib.URIRef('http://example.com/me/tag#1234'), rdflib.RDF.type, rdflib.URIRef('https://schema.bsfs.io/core/Tag')), + (rdflib.URIRef('http://example.com/me/tag#4321'), rdflib.RDF.type, rdflib.URIRef('https://schema.bsfs.io/core/Tag')), (rdflib.URIRef('http://example.com/me/tag#1234'), rdflib.URIRef(self.t_created.uri), rdflib.Literal(t_tag_created, datatype=rdflib.XSD.integer)), (rdflib.URIRef('http://example.com/me/tag#4321'), rdflib.URIRef(self.t_created.uri), rdflib.Literal(t_tag_created, datatype=rdflib.XSD.integer)), }) def test___set(self): # setup - nodes = Nodes(self.backend, self.user, self.ent_type, {URI('http://example.com/me/entity#1234'), URI('http://example.com/me/entity#4321')}) - self.assertSetEqual(set(self.backend._graph), set()) + nodes = Nodes(self.backend, self.ac, self.ent_type, {URI('http://example.com/me/entity#1234'), URI('http://example.com/me/entity#4321')}) + self.assertSetEqual(set(self.backend._graph), self.schema_triples | set()) set_ = nodes._Nodes__set # node_type must match predicate's domain @@ -217,10 +254,10 @@ class TestNodes(unittest.TestCase): time_triples = list(self.backend._graph.objects(rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(self.t_created.uri))) t_ent_created = float(time_triples[0]) if len(time_triples) > 0 else 0.0 # verify triples - self.assertSetEqual(set(self.backend._graph), { + self.assertSetEqual(set(self.backend._graph), self.schema_triples | { # entity definitions - (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.RDF.type, rdflib.URIRef('http://bsfs.ai/schema/Entity')), - (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.RDF.type, rdflib.URIRef('http://bsfs.ai/schema/Entity')), + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.RDF.type, rdflib.URIRef('https://schema.bsfs.io/core/Entity')), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.RDF.type, rdflib.URIRef('https://schema.bsfs.io/core/Entity')), # bookkeeping (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(self.t_created.uri), rdflib.Literal(t_ent_created, datatype=rdflib.XSD.integer)), (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(self.t_created.uri), rdflib.Literal(t_ent_created, datatype=rdflib.XSD.integer)), @@ -230,23 +267,23 @@ class TestNodes(unittest.TestCase): }) # set node value - tags = Nodes(self.backend, self.user, self.tag_type, {URI('http://example.com/me/tag#1234'), URI('http://example.com/me/tag#4321')}) + tags = Nodes(self.backend, self.ac, self.tag_type, {URI('http://example.com/me/tag#1234'), URI('http://example.com/me/tag#4321')}) set_(self.p_tag.uri, tags) # get creation time from backend manually time_triples = list(self.backend._graph.objects(rdflib.URIRef('http://example.com/me/tag#1234'), rdflib.URIRef(self.t_created.uri))) t_tag_created = float(time_triples[0]) if len(time_triples) > 0 else 0.0 # verify triples - self.assertSetEqual(set(self.backend._graph), { + self.assertSetEqual(set(self.backend._graph), self.schema_triples | { # previous values - (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.RDF.type, rdflib.URIRef('http://bsfs.ai/schema/Entity')), - (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.RDF.type, rdflib.URIRef('http://bsfs.ai/schema/Entity')), + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.RDF.type, rdflib.URIRef('https://schema.bsfs.io/core/Entity')), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.RDF.type, rdflib.URIRef('https://schema.bsfs.io/core/Entity')), (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(self.p_filesize.uri), rdflib.Literal(1234, datatype=rdflib.XSD.integer)), (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(self.p_filesize.uri), rdflib.Literal(1234, datatype=rdflib.XSD.integer)), (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(self.t_created.uri), rdflib.Literal(t_ent_created, datatype=rdflib.XSD.integer)), (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(self.t_created.uri), rdflib.Literal(t_ent_created, datatype=rdflib.XSD.integer)), # tag definitions - (rdflib.URIRef('http://example.com/me/tag#1234'), rdflib.RDF.type, rdflib.URIRef('http://bsfs.ai/schema/Tag')), - (rdflib.URIRef('http://example.com/me/tag#4321'), rdflib.RDF.type, rdflib.URIRef('http://bsfs.ai/schema/Tag')), + (rdflib.URIRef('http://example.com/me/tag#1234'), rdflib.RDF.type, rdflib.URIRef('https://schema.bsfs.io/core/Tag')), + (rdflib.URIRef('http://example.com/me/tag#4321'), rdflib.RDF.type, rdflib.URIRef('https://schema.bsfs.io/core/Tag')), # tag bookkeeping (rdflib.URIRef('http://example.com/me/tag#1234'), rdflib.URIRef(self.t_created.uri), rdflib.Literal(t_tag_created, datatype=rdflib.XSD.integer)), (rdflib.URIRef('http://example.com/me/tag#4321'), rdflib.URIRef(self.t_created.uri), rdflib.Literal(t_tag_created, datatype=rdflib.XSD.integer)), @@ -262,29 +299,29 @@ class TestNodes(unittest.TestCase): self.assertRaises(TypeError, set_, self.p_tag.uri, URI('http://example.com/me/tag#1234')) # value's node_type must match the predicate's range self.assertRaises(errors.ConsistencyError, set_, self.p_tag.uri, - Nodes(self.backend, self.user, self.ent_type, self.ent_ids)) + Nodes(self.backend, self.ac, self.ent_type, self.ent_ids)) def test_set(self): - self.assertSetEqual(set(self.backend._graph), set()) - nodes = Nodes(self.backend, self.user, self.ent_type, self.ent_ids) + self.assertSetEqual(set(self.backend._graph), self.schema_triples | set()) + nodes = Nodes(self.backend, self.ac, self.ent_type, self.ent_ids) # can set literal values self.assertEqual(nodes, nodes.set(self.p_filesize.uri, 1234)) self.assertTrue(set(self.backend._graph).issuperset({ # nodes exist - (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.RDF.type, rdflib.URIRef('http://bsfs.ai/schema/Entity')), - (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.RDF.type, rdflib.URIRef('http://bsfs.ai/schema/Entity')), + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.RDF.type, rdflib.URIRef('https://schema.bsfs.io/core/Entity')), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.RDF.type, rdflib.URIRef('https://schema.bsfs.io/core/Entity')), # links exist (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(self.p_filesize.uri), rdflib.Literal(1234, datatype=rdflib.XSD.integer)), (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(self.p_filesize.uri), rdflib.Literal(1234, datatype=rdflib.XSD.integer)), })) # can set node values - self.assertEqual(nodes, nodes.set(self.p_tag.uri, Nodes(self.backend, self.user, self.tag_type, self.tag_ids))) + self.assertEqual(nodes, nodes.set(self.p_tag.uri, Nodes(self.backend, self.ac, self.tag_type, self.tag_ids))) self.assertTrue(set(self.backend._graph).issuperset({ # nodes exist - (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.RDF.type, rdflib.URIRef('http://bsfs.ai/schema/Entity')), - (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.RDF.type, rdflib.URIRef('http://bsfs.ai/schema/Entity')), - (rdflib.URIRef('http://example.com/me/tag#1234'), rdflib.RDF.type, rdflib.URIRef('http://bsfs.ai/schema/Tag')), - (rdflib.URIRef('http://example.com/me/tag#4321'), rdflib.RDF.type, rdflib.URIRef('http://bsfs.ai/schema/Tag')), + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.RDF.type, rdflib.URIRef('https://schema.bsfs.io/core/Entity')), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.RDF.type, rdflib.URIRef('https://schema.bsfs.io/core/Entity')), + (rdflib.URIRef('http://example.com/me/tag#1234'), rdflib.RDF.type, rdflib.URIRef('https://schema.bsfs.io/core/Tag')), + (rdflib.URIRef('http://example.com/me/tag#4321'), rdflib.RDF.type, rdflib.URIRef('https://schema.bsfs.io/core/Tag')), # links exist (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(self.p_tag.uri), rdflib.URIRef('http://example.com/me/tag#1234')), (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(self.p_tag.uri), rdflib.URIRef('http://example.com/me/tag#4321')), @@ -307,24 +344,28 @@ class TestNodes(unittest.TestCase): self.assertSetEqual(curr, set(self.backend._graph)) # cannot assing multiple values to unique predicate self.assertRaises(ValueError, nodes.set, self.p_author.uri, - Nodes(self.backend, self.user, self.user_type, {URI('http://example.com/me/user#1234'), URI('http://example.com/me/user#4321')})) + Nodes(self.backend, self.ac, self.user_type, {URI('http://example.com/me/user#1234'), URI('http://example.com/me/user#4321')})) self.assertSetEqual(curr, set(self.backend._graph)) + # can set on empty nodes + nodes = Nodes(self.backend, self.ac, self.ent_type, {}) + self.assertEqual(nodes, nodes.set(self.p_filesize.uri, 1234)) + def test_set_from_iterable(self): - self.assertSetEqual(set(self.backend._graph), set()) - nodes = Nodes(self.backend, self.user, self.ent_type, self.ent_ids) + self.assertSetEqual(set(self.backend._graph), self.schema_triples | set()) + nodes = Nodes(self.backend, self.ac, self.ent_type, self.ent_ids) # can set literal and node values simultaneously self.assertEqual(nodes, nodes.set_from_iterable({ self.p_filesize.uri: 1234, - self.p_tag.uri: Nodes(self.backend, self.user, self.tag_type, self.tag_ids), + self.p_tag.uri: Nodes(self.backend, self.ac, self.tag_type, self.tag_ids), }.items())) self.assertTrue(set(self.backend._graph).issuperset({ # nodes exist - (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.RDF.type, rdflib.URIRef('http://bsfs.ai/schema/Entity')), - (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.RDF.type, rdflib.URIRef('http://bsfs.ai/schema/Entity')), - (rdflib.URIRef('http://example.com/me/tag#1234'), rdflib.RDF.type, rdflib.URIRef('http://bsfs.ai/schema/Tag')), - (rdflib.URIRef('http://example.com/me/tag#4321'), rdflib.RDF.type, rdflib.URIRef('http://bsfs.ai/schema/Tag')), + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.RDF.type, rdflib.URIRef('https://schema.bsfs.io/core/Entity')), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.RDF.type, rdflib.URIRef('https://schema.bsfs.io/core/Entity')), + (rdflib.URIRef('http://example.com/me/tag#1234'), rdflib.RDF.type, rdflib.URIRef('https://schema.bsfs.io/core/Tag')), + (rdflib.URIRef('http://example.com/me/tag#4321'), rdflib.RDF.type, rdflib.URIRef('https://schema.bsfs.io/core/Tag')), # links exist (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(self.p_filesize.uri), rdflib.Literal(1234, datatype=rdflib.XSD.integer)), (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(self.p_filesize.uri), rdflib.Literal(1234, datatype=rdflib.XSD.integer)), @@ -349,9 +390,254 @@ class TestNodes(unittest.TestCase): self.assertSetEqual(curr, set(self.backend._graph)) # cannot assing multiple values to unique predicate self.assertRaises(ValueError, nodes.set_from_iterable, ((self.p_filesize.uri, 1234), - (self.p_author.uri, Nodes(self.backend, self.user, self.user_type, {URI('http://example.com/me/user#1234'), URI('http://example.com/me/user#4321')})))) + (self.p_author.uri, Nodes(self.backend, self.ac, self.user_type, {URI('http://example.com/me/user#1234'), URI('http://example.com/me/user#4321')})))) self.assertSetEqual(curr, set(self.backend._graph)) + # can set on empty nodes + nodes = Nodes(self.backend, self.ac, self.ent_type, {}) + self.assertEqual(nodes, nodes.set_from_iterable([(self.p_filesize.uri, 1234)])) + + + def test_get(self): + # setup: add some instances + Nodes(self.backend, self.ac, self.ent_type, {'http://example.com/me/entity#1234'}) \ + .set(ns.bse.comment, 'hello world') \ + .set(ns.bse.filesize, 1234) \ + .set(ns.bse.tag, Nodes(self.backend, self.ac, self.tag_type, {'http://example.com/me/tag#1234'})) + Nodes(self.backend, self.ac, self.ent_type, {'http://example.com/me/entity#4321'}) \ + .set(ns.bse.filesize, 4321) \ + .set(ns.bse.tag, Nodes(self.backend, self.ac, self.tag_type, {'http://example.com/me/tag#4321'})) + Nodes(self.backend, self.ac, self.tag_type, {'http://example.com/me/tag#1234'}) \ + .set(ns.bst.label, 'tag_label_1234') + Nodes(self.backend, self.ac, self.tag_type, {'http://example.com/me/tag#4321'}) \ + .set(ns.bst.label, 'tag_label_4321') + # setup: get nodes instance + nodes = Nodes(self.backend, self.ac, self.ent_type, self.ent_ids) + + # must pass at least one path + self.assertRaises(AttributeError, nodes.get) + # view must be list or dict + self.assertRaises(ValueError, nodes.get, ns.bse.filesize, view='hello') + self.assertRaises(ValueError, nodes.get, ns.bse.filesize, view=1234) + self.assertRaises(ValueError, nodes.get, ns.bse.filesize, view=tuple) + # can pass path as URI + self.assertDictEqual(nodes.get(ns.bse.filesize), { + Nodes(self.backend, self.ac, self.ent_type, {'http://example.com/me/entity#1234'}): 1234, + Nodes(self.backend, self.ac, self.ent_type, {'http://example.com/me/entity#4321'}): 4321, + }) + # can pass path as sequence of URI + self.assertDictEqual(nodes.get((ns.bse.tag, ns.bst.label)), { + Nodes(self.backend, self.ac, self.ent_type, {'http://example.com/me/entity#1234'}): {'tag_label_1234'}, + Nodes(self.backend, self.ac, self.ent_type, {'http://example.com/me/entity#4321'}): {'tag_label_4321'}, + }) + # get returns the same path that was passed + self.assertCountEqual(list(nodes.get((ns.bse.tag, ns.bst.label), path=True, view=list)), [ + (Nodes(self.backend, self.ac, self.ent_type, {'http://example.com/me/entity#1234'}), (ns.bse.tag, ns.bst.label), 'tag_label_1234'), + (Nodes(self.backend, self.ac, self.ent_type, {'http://example.com/me/entity#4321'}), (ns.bse.tag, ns.bst.label), 'tag_label_4321'), + ]) + self.assertCountEqual(list(nodes.get([ns.bse.tag, ns.bst.label], path=True, view=list)), [ + (Nodes(self.backend, self.ac, self.ent_type, {'http://example.com/me/entity#1234'}), [ns.bse.tag, ns.bst.label], 'tag_label_1234'), + (Nodes(self.backend, self.ac, self.ent_type, {'http://example.com/me/entity#4321'}), [ns.bse.tag, ns.bst.label], 'tag_label_4321'), + ]) + # paths must be URI or sequence thereof + self.assertRaises(TypeError, nodes.get, 1234) + self.assertRaises(TypeError, nodes.get, (ns.bse.tag, 1234)) + self.assertRaises(TypeError, nodes.get, (1234, ns.bse.tag)) + self.assertRaises(ValueError, nodes.get, 'hello world') + self.assertRaises(errors.ConsistencyError, nodes.get, 'hello_world') + self.assertRaises(errors.ConsistencyError, nodes.get, ns.bse.invalid) + self.assertRaises(errors.ConsistencyError, nodes.get, (ns.bse.tag, ns.bst.invalid)) + # can pass multiple paths + self.assertDictEqual(nodes.get(ns.bse.filesize, (ns.bse.tag, ns.bst.label)), { + Nodes(self.backend, self.ac, self.ent_type, {'http://example.com/me/entity#1234'}): { + ns.bse.filesize: 1234, + (ns.bse.tag, ns.bst.label): {'tag_label_1234'}, + }, + Nodes(self.backend, self.ac, self.ent_type, {'http://example.com/me/entity#4321'}): { + ns.bse.filesize: 4321, + (ns.bse.tag, ns.bst.label): {'tag_label_4321'}, + }, + }) + # get respects view + self.assertDictEqual(nodes.get(ns.bse.filesize, view=dict), { + Nodes(self.backend, self.ac, self.ent_type, {'http://example.com/me/entity#1234'}): 1234, + Nodes(self.backend, self.ac, self.ent_type, {'http://example.com/me/entity#4321'}): 4321, + }) + self.assertSetEqual(set(nodes.get(ns.bse.filesize, view=list)), { + (Nodes(self.backend, self.ac, self.ent_type, {'http://example.com/me/entity#1234'}), 1234), + (Nodes(self.backend, self.ac, self.ent_type, {'http://example.com/me/entity#4321'}), 4321), + }) + # get returns Nodes instance when fetching a node + self.assertDictEqual(nodes.get(ns.bse.tag), { + Nodes(self.backend, self.ac, self.ent_type, {'http://example.com/me/entity#1234'}): + {Nodes(self.backend, self.ac, self.tag_type, {'http://example.com/me/tag#1234'})}, + Nodes(self.backend, self.ac, self.ent_type, {'http://example.com/me/entity#4321'}): + {Nodes(self.backend, self.ac, self.tag_type, {'http://example.com/me/tag#4321'})}, + }) + # get returns a value when fetching a value and omits missing values + self.assertDictEqual(nodes.get(ns.bse.comment), { + Nodes(self.backend, self.ac, self.ent_type, {'http://example.com/me/entity#1234'}): {'hello world'}, + }) + + # results can be empty + nodes = Nodes(self.backend, self.ac, self.ent_type, {'http://example.com/me/entity#4321'}) # has filesize, tag but no comment + # unique paths return the default value + self.assertIsNone(nodes.get(ns.bse.author)) + self.assertEqual(nodes.get(ns.bse.author, default=1234), 1234) + # non-unique paths return an empty set + self.assertSetEqual(nodes.get(ns.bse.comment), set()) + + # nodes can have no guids + nodes = Nodes(self.backend, self.ac, self.ent_type, set()) + # empty nodes does not excuse an invalid request + self.assertRaises(TypeError, nodes.get, 1234) + self.assertRaises(errors.ConsistencyError, nodes.get, ns.bse.invalid) + # list view always returns an empty list + self.assertListEqual(list(nodes.get(ns.bse.comment, view=list)), []) + self.assertListEqual(list(nodes.get(ns.bse.comment, view=list, node=True)), []) + self.assertListEqual(list(nodes.get(ns.bse.comment, view=list, path=True)), []) + self.assertListEqual(list(nodes.get(ns.bse.comment, view=list, node=True, path=True, value=True)), []) + self.assertListEqual(list(nodes.get(ns.bse.comment, view=list, node=False)), []) + self.assertListEqual(list(nodes.get(ns.bse.comment, view=list, path=False)), []) + self.assertListEqual(list(nodes.get(ns.bse.comment, view=list, node=False, path=False, value=False)), []) + self.assertListEqual(list(nodes.get(ns.bse.comment, ns.bse.filesize, view=list)), []) + self.assertListEqual(list(nodes.get(ns.bse.comment, ns.bse.filesize, view=list, node=True)), []) + self.assertListEqual(list(nodes.get(ns.bse.comment, ns.bse.filesize, view=list, path=True)), []) + self.assertListEqual(list(nodes.get(ns.bse.comment, ns.bse.filesize, view=list, node=True, path=True, value=True)), []) + self.assertListEqual(list(nodes.get(ns.bse.comment, ns.bse.filesize, view=list, node=False)), []) + self.assertListEqual(list(nodes.get(ns.bse.comment, ns.bse.filesize, view=list, path=False)), []) + self.assertListEqual(list(nodes.get(ns.bse.comment, ns.bse.filesize, view=list, node=False, path=False, value=False)), []) + # dict view returns an empty dict or an empty set + self.assertDictEqual(nodes.get(ns.bse.comment, view=dict), {}) + self.assertDictEqual(nodes.get(ns.bse.comment, view=dict, node=True), {}) + self.assertDictEqual(nodes.get(ns.bse.comment, view=dict, path=True), {}) + self.assertDictEqual(nodes.get(ns.bse.comment, view=dict, node=True, path=True, value=True, default=None), {}) + self.assertSetEqual(nodes.get(ns.bse.comment, view=dict, node=False), set()) + self.assertDictEqual(nodes.get(ns.bse.comment, view=dict, path=False), {}) + self.assertSetEqual(nodes.get(ns.bse.comment, view=dict, node=False, path=False), set()) + self.assertSetEqual(nodes.get(ns.bse.comment, view=dict, node=False, path=False, value=False, default=None), set()) + self.assertDictEqual(nodes.get(ns.bse.comment, ns.bse.filesize, view=dict), {}) + self.assertDictEqual(nodes.get(ns.bse.comment, ns.bse.filesize, view=dict, node=True), {}) + self.assertDictEqual(nodes.get(ns.bse.comment, ns.bse.filesize, view=dict, path=True), {}) + self.assertDictEqual(nodes.get(ns.bse.comment, ns.bse.filesize, view=dict, node=True, path=True, value=True, default=None), {}) + self.assertDictEqual(nodes.get(ns.bse.comment, ns.bse.filesize, view=dict, node=False), {}) + self.assertDictEqual(nodes.get(ns.bse.comment, ns.bse.filesize, view=dict, path=False), {}) + self.assertSetEqual(nodes.get(ns.bse.comment, ns.bse.filesize, view=dict, node=False, path=False), set()) + self.assertSetEqual(nodes.get(ns.bse.comment, ns.bse.filesize, view=dict, node=False, path=False, value=False, default=None), set()) + + + def test_getattr(self): + nodes = Nodes(self.backend, self.ac, self.ent_type, {'http://example.com/me/entity#1234'}) + # can get walks to values + self.assertEqual(nodes.filesize, Walk(nodes, (self.p_filesize, ))) + # can get walks to nodes + self.assertEqual(nodes.tag, Walk(nodes, (self.p_tag, ))) + # can do multiple hops + self.assertEqual(nodes.tag.label, Walk(nodes, (self.p_tag, self.p_label))) + # invalid step raises an error + self.assertRaises(ValueError, getattr, nodes, 'foobar') + + def test_schema(self): + self.assertEqual(Nodes(self.backend, self.ac, self.ent_type, + {URI('http://example.com/me/entity#1234')}).schema, self.backend.schema) + + def test_operators(self): # __add__, __or__, __sub__, __and__ + gen = partial(Nodes, self.backend, self.ac, self.ent_type) + nodes = gen({URI('http://example.com/me/entity#1234')}) + # add/or concatenates guids + self.assertEqual( + gen({URI('http://example.com/me/entity#1234')}) + + gen({URI('http://example.com/me/entity#4321')}), + # target + gen({ + URI('http://example.com/me/entity#1234'), + URI('http://example.com/me/entity#4321')})) + self.assertEqual( + gen({URI('http://example.com/me/entity#1234')}) | + gen({URI('http://example.com/me/entity#4321')}), + # target + gen({ + URI('http://example.com/me/entity#1234'), + URI('http://example.com/me/entity#4321')})) + # repeated guids are ignored + self.assertEqual( + gen({URI('http://example.com/me/entity#1234')}) + + gen({URI('http://example.com/me/entity#1234')}), + # target + gen({URI('http://example.com/me/entity#1234')})) + self.assertEqual( + gen({URI('http://example.com/me/entity#1234')}) | + gen({URI('http://example.com/me/entity#1234')}), + # target + gen({URI('http://example.com/me/entity#1234')})) + + # sub substracts guids + self.assertEqual( + gen({URI('http://example.com/me/entity#1234'), + URI('http://example.com/me/entity#4321')}) - + gen({URI('http://example.com/me/entity#4321')}), + # target + gen({URI('http://example.com/me/entity#1234')})) + # missing guids are ignored + self.assertEqual( + gen({URI('http://example.com/me/entity#1234')}) - + gen({URI('http://example.com/me/entity#4321')}), + # target + gen({URI('http://example.com/me/entity#1234')})) + + # and intersects guids + self.assertEqual( + gen({URI('http://example.com/me/entity#1234'), + URI('http://example.com/me/entity#4321')}) & + gen({URI('http://example.com/me/entity#4321'), + URI('http://example.com/me/entity#5678')}), + # target + gen({URI('http://example.com/me/entity#4321')})) + + for op in (operator.add, operator.or_, operator.sub, operator.and_): + # type must match + self.assertRaises(TypeError, op, nodes, 1234) + self.assertRaises(TypeError, op, nodes, 'hello world') + # backend must match + self.assertRaises(ValueError, op, nodes, + Nodes(None, self.ac, self.ent_type, {URI('http://example.com/me/entity#1234')})) + # ac must match + self.assertRaises(ValueError, op, nodes, + Nodes(self.backend, NullAC(self.backend, ''), + self.ent_type, {URI('http://example.com/me/entity#1234')})) + # node type must match + self.assertRaises(ValueError, op, nodes, + Nodes(self.backend, self.ac, self.tag_type, {URI('http://example.com/me/entity#1234')})) + + def test_len(self): + self.assertEqual(1, len(Nodes(self.backend, self.ac, self.ent_type, { + URI('http://example.com/me/entity#1234'), + }))) + self.assertEqual(2, len(Nodes(self.backend, self.ac, self.ent_type, { + URI('http://example.com/me/entity#1234'), + URI('http://example.com/me/entity#4321'), + }))) + self.assertEqual(4, len(Nodes(self.backend, self.ac, self.ent_type, { + URI('http://example.com/me/entity#1234'), + URI('http://example.com/me/entity#4321'), + URI('http://example.com/me/entity#5678'), + URI('http://example.com/me/entity#8765'), + }))) + + def test_iter(self): # __iter__ + gen = partial(Nodes, self.backend, self.ac, self.ent_type) + self.assertSetEqual(set(Nodes(self.backend, self.ac, self.ent_type, { + URI('http://example.com/me/entity#1234'), + URI('http://example.com/me/entity#4321'), + URI('http://example.com/me/entity#5678'), + URI('http://example.com/me/entity#8765'), + })), { + gen({URI('http://example.com/me/entity#1234')}), + gen({URI('http://example.com/me/entity#4321')}), + gen({URI('http://example.com/me/entity#5678')}), + gen({URI('http://example.com/me/entity#8765')}), + }) + ## main ## diff --git a/test/graph/test_resolve.py b/test/graph/test_resolve.py new file mode 100644 index 0000000..e09b1cc --- /dev/null +++ b/test/graph/test_resolve.py @@ -0,0 +1,199 @@ + +# imports +import unittest + +# bsie imports +from bsfs import schema as bsc +from bsfs.graph import Graph, nodes +from bsfs.namespace import ns +from bsfs.query import ast +from bsfs.triple_store import SparqlStore +from bsfs.utils import URI, errors + +# objects to test +from bsfs.graph.resolve import Filter + + +## code ## + +ns.bse = ns.bsfs.Entity() + +class TestFilter(unittest.TestCase): + """ + + NOTE: The Filter resolver is relatively simple as it only checks and changes + ast.filter.Is instances. Hence, we don't test all methods individually but + all of them with respect to ast.filter.Is elements. + + """ + + def test_call(self): # tests resolve implicitly + schema = bsc.from_string(''' + prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> + prefix xsd: <http://www.w3.org/2001/XMLSchema#> + + prefix bsfs: <https://schema.bsfs.io/core/> + prefix bse: <https://schema.bsfs.io/core/Entity#> + prefix bsl: <https://schema.bsfs.io/core/Literal/> + prefix bsa: <https://schema.bsfs.io/core/Literal/Array/> + + bsfs:Entity rdfs:subClassOf bsfs:Node . + bsfs:Tag rdfs:subClassOf bsfs:Node . + xsd:string rdfs:subClassOf bsfs:Literal . + bsl:Number rdfs:subClassOf bsfs:Literal . + bsl:Array rdfs:subClassOf bsfs:Literal . + bsa:Feature rdfs:subClassOf bsl:Array . + xsd:integer rdfs:subClassOf bsl:Number . + + bsfs:Colors rdfs:subClassOf bsa:Feature ; + bsfs:dimension "5"^^xsd:integer . + + bse:colors rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity ; + rdfs:range bsfs:Colors . + + bse:comment rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity ; + rdfs:range xsd:string ; + bsfs:unique "false"^^xsd:boolean . + + bse:filesize rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity ; + rdfs:range xsd:integer ; + bsfs:unique "false"^^xsd:boolean . + + bse:tag rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity ; + rdfs:range bsfs:Tag ; + bsfs:unique "false"^^xsd:boolean . + ''') + backend = SparqlStore.Open() + backend.schema = schema + graph = Graph(backend, URI('http://example.com/me')) + ents = graph.nodes(ns.bsfs.Entity, + {URI('http://example.com/me/entity#1234'), URI('http://example.com/me/entity#4321')}) + tags = graph.nodes(ns.bsfs.Tag, + {URI('http://example.com/me/tag#1234'), URI('http://example.com/me/tag#4321')}) + invalid = nodes.Nodes(None, '', schema.node(ns.bsfs.Node).child(ns.bsfs.Invalid), + {'http://example.com/you/invalid#1234', 'http://example.com/you/invalid#4321'}) + resolver = Filter(schema) + + # query can be None + self.assertIsNone(resolver(schema.node(ns.bsfs.Entity), None)) + + # immediate Is + self.assertEqual(resolver(schema.node(ns.bsfs.Entity), + ast.filter.Is(ents)), + ast.filter.Or( + ast.filter.Is('http://example.com/me/entity#1234'), + ast.filter.Is('http://example.com/me/entity#4321') + )) + # only resolves nodes instances, not URIs + self.assertEqual(resolver(schema.node(ns.bsfs.Entity), + ast.filter.Is('http://example.com/me/entity#1234')), + ast.filter.Is('http://example.com/me/entity#1234')) + self.assertEqual(resolver(schema.node(ns.bsfs.Entity), + ast.filter.Is(1234)), + ast.filter.Is(1234)) + + # within And (also checks _value) + self.assertEqual(resolver(schema.node(ns.bsfs.Entity), + ast.filter.And( + ast.filter.Is(ents), + ast.filter.Any(ns.bse.comment, ast.filter.Equals('hello world')), + )), + ast.filter.And( + ast.filter.Or( + ast.filter.Is('http://example.com/me/entity#1234'), + ast.filter.Is('http://example.com/me/entity#4321')), + ast.filter.Any(ns.bse.comment, ast.filter.Equals('hello world')) + )) + # within Or (checks _bounded) + self.assertEqual(resolver(schema.node(ns.bsfs.Entity), + ast.filter.Or( + ast.filter.Is(ents), + ast.filter.Any(ns.bse.filesize, ast.filter.LessThan(5)), + )), + ast.filter.Or( + ast.filter.Or( + ast.filter.Is('http://example.com/me/entity#1234'), + ast.filter.Is('http://example.com/me/entity#4321')), + ast.filter.Any(ns.bse.filesize, ast.filter.LessThan(5)) + )) + + # Any-branched Is + self.assertEqual(resolver(schema.node(ns.bsfs.Entity), + ast.filter.Any(ns.bse.tag, ast.filter.Is(tags))), + ast.filter.Any(ns.bse.tag, ast.filter.Or( + ast.filter.Is('http://example.com/me/tag#1234'), + ast.filter.Is('http://example.com/me/tag#4321')), + )) + # All-branched Is + self.assertEqual(resolver(schema.node(ns.bsfs.Entity), + ast.filter.All(ns.bse.tag, ast.filter.Is(tags))), + ast.filter.All(ns.bse.tag, ast.filter.Or( + ast.filter.Is('http://example.com/me/tag#1234'), + ast.filter.Is('http://example.com/me/tag#4321')), + )) + # Negated predicate + self.assertEqual(resolver(schema.node(ns.bsfs.Tag), + ast.filter.Any(ast.filter.Predicate(ns.bse.tag, reverse=True), ast.filter.Is(ents))), + ast.filter.Any(ast.filter.Predicate(ns.bse.tag, reverse=True), ast.filter.Or( + ast.filter.Is('http://example.com/me/entity#1234'), + ast.filter.Is('http://example.com/me/entity#4321')), + )) + + # negated Is + self.assertEqual(resolver(schema.node(ns.bsfs.Entity), + ast.filter.Not(ast.filter.Is(ents))), + ast.filter.Not( + ast.filter.Or( + ast.filter.Is('http://example.com/me/entity#1234'), + ast.filter.Is('http://example.com/me/entity#4321')), + )) + + # for sake of completeness: Has + self.assertEqual(resolver(schema.node(ns.bsfs.Entity), + ast.filter.Has(ns.bse.comment)), + ast.filter.Has(ns.bse.comment)) + # for sake of completeness: Distance + self.assertEqual(resolver(schema.node(ns.bsfs.Entity), + ast.filter.Any(ns.bse.colors, ast.filter.Distance([1,2,3,4,5], 1))), + ast.filter.Any(ns.bse.colors, ast.filter.Distance([1,2,3,4,5], 1))) + # route errors + self.assertRaises(errors.BackendError, resolver, schema.node(ns.bsfs.Tag), + ast.filter.Predicate(ns.bse.comment)) + self.assertRaises(errors.BackendError, resolver, schema.node(ns.bsfs.Tag), + ast.filter.Any(ast.filter.PredicateExpression(), ast.filter.Equals('foo'))) + self.assertRaises(errors.BackendError, resolver._one_of, ast.filter.OneOf(ast.filter.Predicate(ns.bsfs.Predicate))) + # for sake of coverage completeness: valid OneOf + self.assertIsNotNone(resolver._one_of(ast.filter.OneOf(ast.filter.Predicate(ns.bse.colors)))) + + # check schema consistency + self.assertRaises(errors.ConsistencyError, resolver, schema.node(ns.bsfs.Tag), + ast.filter.Is(invalid)) + # check immediate type compatibility + self.assertRaises(errors.ConsistencyError, resolver, schema.node(ns.bsfs.Tag), + ast.filter.Is(ents)) + self.assertRaises(errors.ConsistencyError, resolver, schema.node(ns.bsfs.Entity), + ast.filter.Is(tags)) + # check type compatibility through branches + self.assertRaises(errors.ConsistencyError, resolver, schema.node(ns.bsfs.Tag), + ast.filter.Any(ns.bse.comment, ast.filter.Is(tags))) + self.assertRaises(errors.ConsistencyError, resolver, schema.node(ns.bsfs.Tag), + ast.filter.Any(ns.bse.invalid, ast.filter.Is(tags))) + self.assertRaises(errors.ConsistencyError, resolver, schema.node(ns.bsfs.Tag), + ast.filter.Any(ast.filter.OneOf(ns.bse.comment, ns.bse.tag), ast.filter.Is(tags))) + self.assertRaises(errors.ConsistencyError, resolver, schema.node(ns.bsfs.Tag), + ast.filter.Any(ast.filter.OneOf(ns.bse.comment, ns.bse.filesize), ast.filter.Is(tags))) + self.assertRaises(errors.ConsistencyError, resolver, schema.node(ns.bsfs.Tag), + ast.filter.Any(ast.filter.Predicate(ns.bse.tag, reverse=True), ast.filter.Is(tags))) + + + +## main ## + +if __name__ == '__main__': + unittest.main() + +## EOF ## diff --git a/test/graph/test_result.py b/test/graph/test_result.py new file mode 100644 index 0000000..8960ef6 --- /dev/null +++ b/test/graph/test_result.py @@ -0,0 +1,429 @@ + +# imports +import unittest + +# bsie imports +from bsfs import schema as bsc +from bsfs.namespace import ns +from bsfs.utils import URI + +# objects to test +from bsfs.graph.result import to_list_view, to_dict_view + + +## code ## + +ns.bse = ns.bsfs.Entity() + +class TestListView(unittest.TestCase): + def setUp(self): + self.triples_111 = [('ent#1234', ns.bse.iso, 123)] + self.triples_11U = [('ent#1234', ns.bse.tag, 'tag#1234'), + ('ent#1234', ns.bse.tag, 'tag#5678')] + self.triples_1M1 = [('ent#1234', ns.bse.iso, 123), + ('ent#1234', ns.bse.t_created, '2010-01-02')] + self.triples_1MU = [('ent#1234', ns.bse.iso, 123), + ('ent#1234', ns.bse.tag, 'tag#1234'), + ('ent#1234', ns.bse.tag, 'tag#5678')] + self.triples_N11 = [('ent#1234', ns.bse.iso, 123), + ('ent#4321', ns.bse.iso, 321)] + self.triples_N1U = [('ent#1234', ns.bse.tag, 'tag#1234'), + ('ent#1234', ns.bse.tag, 'tag#5678'), + ('ent#4321', ns.bse.tag, 'tag#4321')] + self.triples_NM1 = [('ent#1234', ns.bse.iso, 123), + ('ent#1234', ns.bse.t_created, '2010-01-02'), + ('ent#4321', ns.bse.iso, 321), + ('ent#4321', ns.bse.t_created, '2022-02-22')] + self.triples_NMU = [('ent#1234', ns.bse.iso, 123), + ('ent#1234', ns.bse.tag, 'tag#1234'), + ('ent#1234', ns.bse.tag, 'tag#5678'), + ('ent#4321', ns.bse.iso, 321), + ('ent#4321', ns.bse.t_created, '2022-02-22')] + + def test_copy(self): + # iterator yields tuples + self.assertIsInstance(list(to_list_view([('subject', 'predicate', 'object')], node=True, path=True, value=True))[0], tuple) + # components are not changed + class Foo(): pass + foo = Foo() + self.assertListEqual(list(to_list_view([('subject', 'predicate', 'object')], node=True, path=True, value=True)), + [('subject', 'predicate', 'object')]) + self.assertListEqual(list(to_list_view([(foo, 'predicate', 'object')], node=True, path=True, value=True)), + [(foo, 'predicate', 'object')]) + self.assertListEqual(list(to_list_view([('subject', foo, 'object')], node=True, path=True, value=True)), + [('subject', foo, 'object')]) + self.assertListEqual(list(to_list_view([('subject', 'predicate', foo)], node=True, path=True, value=True)), + [('subject', 'predicate', foo)]) + + def test_agg_none(self): + self.assertListEqual(list(to_list_view(self.triples_111, node=True, path=True, value=True)), self.triples_111) + self.assertListEqual(list(to_list_view(self.triples_11U, node=True, path=True, value=True)), self.triples_11U) + self.assertListEqual(list(to_list_view(self.triples_1M1, node=True, path=True, value=True)), self.triples_1M1) + self.assertListEqual(list(to_list_view(self.triples_1MU, node=True, path=True, value=True)), self.triples_1MU) + self.assertListEqual(list(to_list_view(self.triples_N11, node=True, path=True, value=True)), self.triples_N11) + self.assertListEqual(list(to_list_view(self.triples_N1U, node=True, path=True, value=True)), self.triples_N1U) + self.assertListEqual(list(to_list_view(self.triples_NM1, node=True, path=True, value=True)), self.triples_NM1) + self.assertListEqual(list(to_list_view(self.triples_NMU, node=True, path=True, value=True)), self.triples_NMU) + + def test_agg_node(self): + self.assertListEqual(list(to_list_view(self.triples_111, node=False, path=True, value=True)), + [(ns.bse.iso, 123)]) + self.assertListEqual(list(to_list_view(self.triples_11U, node=False, path=True, value=True)), + [(ns.bse.tag, 'tag#1234'), (ns.bse.tag, 'tag#5678')]) + self.assertListEqual(list(to_list_view(self.triples_1M1, node=False, path=True, value=True)), + [(ns.bse.iso, 123), (ns.bse.t_created, '2010-01-02')]) + self.assertListEqual(list(to_list_view(self.triples_1MU, node=False, path=True, value=True)), + [(ns.bse.iso, 123), (ns.bse.tag, 'tag#1234'), (ns.bse.tag, 'tag#5678')]) + self.assertListEqual(list(to_list_view(self.triples_N11, node=False, path=True, value=True)), + [(ns.bse.iso, 123), (ns.bse.iso, 321)]) + self.assertListEqual(list(to_list_view(self.triples_N1U, node=False, path=True, value=True)), + [(ns.bse.tag, 'tag#1234'), (ns.bse.tag, 'tag#5678'), (ns.bse.tag, 'tag#4321')]) + self.assertListEqual(list(to_list_view(self.triples_NM1, node=False, path=True, value=True)), + [(ns.bse.iso, 123), (ns.bse.t_created, '2010-01-02'), (ns.bse.iso, 321), (ns.bse.t_created, '2022-02-22')]) + self.assertListEqual(list(to_list_view(self.triples_NMU, node=False, path=True, value=True)), + [(ns.bse.iso, 123), (ns.bse.tag, 'tag#1234'), (ns.bse.tag, 'tag#5678'), (ns.bse.iso, 321), (ns.bse.t_created, '2022-02-22')]) + + def test_agg_path(self): + self.assertListEqual(list(to_list_view(self.triples_111, node=True, path=False, value=True)), + [('ent#1234', 123)]) + self.assertListEqual(list(to_list_view(self.triples_11U, node=True, path=False, value=True)), + [('ent#1234', 'tag#1234'), ('ent#1234', 'tag#5678')]) + self.assertListEqual(list(to_list_view(self.triples_1M1, node=True, path=False, value=True)), + [('ent#1234', 123), ('ent#1234', '2010-01-02')]) + self.assertListEqual(list(to_list_view(self.triples_1MU, node=True, path=False, value=True)), + [('ent#1234', 123), ('ent#1234', 'tag#1234'), ('ent#1234', 'tag#5678')]) + self.assertListEqual(list(to_list_view(self.triples_N11, node=True, path=False, value=True)), + [('ent#1234', 123), ('ent#4321', 321)]) + self.assertListEqual(list(to_list_view(self.triples_N1U, node=True, path=False, value=True)), + [('ent#1234', 'tag#1234'), ('ent#1234', 'tag#5678'), ('ent#4321', 'tag#4321')]) + self.assertListEqual(list(to_list_view(self.triples_NM1, node=True, path=False, value=True)), + [('ent#1234', 123), ('ent#1234', '2010-01-02'), ('ent#4321', 321), ('ent#4321', '2022-02-22')]) + self.assertListEqual(list(to_list_view(self.triples_NMU, node=True, path=False, value=True)), + [('ent#1234', 123), ('ent#1234', 'tag#1234'), ('ent#1234', 'tag#5678'), ('ent#4321', 321), ('ent#4321', '2022-02-22')]) + + def test_agg_node_path(self): + self.assertListEqual(list(to_list_view(self.triples_111, node=False, path=False, value=True)), + [123]) + self.assertListEqual(list(to_list_view(self.triples_11U, node=False, path=False, value=True)), + ['tag#1234', 'tag#5678']) + self.assertListEqual(list(to_list_view(self.triples_1M1, node=False, path=False, value=True)), + [123, '2010-01-02']) + self.assertListEqual(list(to_list_view(self.triples_1MU, node=False, path=False, value=True)), + [123, 'tag#1234', 'tag#5678']) + self.assertListEqual(list(to_list_view(self.triples_N11, node=False, path=False, value=True)), + [123, 321]) + self.assertListEqual(list(to_list_view(self.triples_N1U, node=False, path=False, value=True)), + ['tag#1234', 'tag#5678', 'tag#4321']) + self.assertListEqual(list(to_list_view(self.triples_NM1, node=False, path=False, value=True)), + [123, '2010-01-02', 321, '2022-02-22']) + self.assertListEqual(list(to_list_view(self.triples_NMU, node=False, path=False, value=True)), + [123, 'tag#1234', 'tag#5678', 321, '2022-02-22']) + + def test_agg_value(self): + # value flag has no effect + self.assertListEqual(list(to_list_view(self.triples_111, node=True, path=True, value=True)), self.triples_111) + self.assertListEqual(list(to_list_view(self.triples_11U, node=True, path=True, value=True)), self.triples_11U) + self.assertListEqual(list(to_list_view(self.triples_1M1, node=True, path=True, value=True)), self.triples_1M1) + self.assertListEqual(list(to_list_view(self.triples_1MU, node=True, path=True, value=True)), self.triples_1MU) + self.assertListEqual(list(to_list_view(self.triples_N11, node=True, path=True, value=True)), self.triples_N11) + self.assertListEqual(list(to_list_view(self.triples_N1U, node=True, path=True, value=True)), self.triples_N1U) + self.assertListEqual(list(to_list_view(self.triples_NM1, node=True, path=True, value=True)), self.triples_NM1) + self.assertListEqual(list(to_list_view(self.triples_NMU, node=True, path=True, value=True)), self.triples_NMU) + + def test_agg_node_value(self): + # value flag has no effect -> same test as test_agg_node + self.assertListEqual(list(to_list_view(self.triples_111, node=False, path=True, value=False)), + [(ns.bse.iso, 123)]) + self.assertListEqual(list(to_list_view(self.triples_11U, node=False, path=True, value=False)), + [(ns.bse.tag, 'tag#1234'), (ns.bse.tag, 'tag#5678')]) + self.assertListEqual(list(to_list_view(self.triples_1M1, node=False, path=True, value=False)), + [(ns.bse.iso, 123), (ns.bse.t_created, '2010-01-02')]) + self.assertListEqual(list(to_list_view(self.triples_1MU, node=False, path=True, value=False)), + [(ns.bse.iso, 123), (ns.bse.tag, 'tag#1234'), (ns.bse.tag, 'tag#5678')]) + self.assertListEqual(list(to_list_view(self.triples_N11, node=False, path=True, value=False)), + [(ns.bse.iso, 123), (ns.bse.iso, 321)]) + self.assertListEqual(list(to_list_view(self.triples_N1U, node=False, path=True, value=False)), + [(ns.bse.tag, 'tag#1234'), (ns.bse.tag, 'tag#5678'), (ns.bse.tag, 'tag#4321')]) + self.assertListEqual(list(to_list_view(self.triples_NM1, node=False, path=True, value=False)), + [(ns.bse.iso, 123), (ns.bse.t_created, '2010-01-02'), (ns.bse.iso, 321), (ns.bse.t_created, '2022-02-22')]) + self.assertListEqual(list(to_list_view(self.triples_NMU, node=False, path=True, value=False)), + [(ns.bse.iso, 123), (ns.bse.tag, 'tag#1234'), (ns.bse.tag, 'tag#5678'), (ns.bse.iso, 321), (ns.bse.t_created, '2022-02-22')]) + + def test_agg_path_value(self): + # value flag has no effect -> same test as test_agg_path + self.assertListEqual(list(to_list_view(self.triples_111, node=True, path=False, value=False)), + [('ent#1234', 123)]) + self.assertListEqual(list(to_list_view(self.triples_11U, node=True, path=False, value=False)), + [('ent#1234', 'tag#1234'), ('ent#1234', 'tag#5678')]) + self.assertListEqual(list(to_list_view(self.triples_1M1, node=True, path=False, value=False)), + [('ent#1234', 123), ('ent#1234', '2010-01-02')]) + self.assertListEqual(list(to_list_view(self.triples_1MU, node=True, path=False, value=False)), + [('ent#1234', 123), ('ent#1234', 'tag#1234'), ('ent#1234', 'tag#5678')]) + self.assertListEqual(list(to_list_view(self.triples_N11, node=True, path=False, value=False)), + [('ent#1234', 123), ('ent#4321', 321)]) + self.assertListEqual(list(to_list_view(self.triples_N1U, node=True, path=False, value=False)), + [('ent#1234', 'tag#1234'), ('ent#1234', 'tag#5678'), ('ent#4321', 'tag#4321')]) + self.assertListEqual(list(to_list_view(self.triples_NM1, node=True, path=False, value=False)), + [('ent#1234', 123), ('ent#1234', '2010-01-02'), ('ent#4321', 321), ('ent#4321', '2022-02-22')]) + self.assertListEqual(list(to_list_view(self.triples_NMU, node=True, path=False, value=False)), + [('ent#1234', 123), ('ent#1234', 'tag#1234'), ('ent#1234', 'tag#5678'), ('ent#4321', 321), ('ent#4321', '2022-02-22')]) + + def test_agg_all(self): + # value flag has no effect -> same test as test_agg_node_path + self.assertListEqual(list(to_list_view(self.triples_111, node=False, path=False, value=False)), + [123]) + self.assertListEqual(list(to_list_view(self.triples_11U, node=False, path=False, value=False)), + ['tag#1234', 'tag#5678']) + self.assertListEqual(list(to_list_view(self.triples_1M1, node=False, path=False, value=False)), + [123, '2010-01-02']) + self.assertListEqual(list(to_list_view(self.triples_1MU, node=False, path=False, value=False)), + [123, 'tag#1234', 'tag#5678']) + self.assertListEqual(list(to_list_view(self.triples_N11, node=False, path=False, value=False)), + [123, 321]) + self.assertListEqual(list(to_list_view(self.triples_N1U, node=False, path=False, value=False)), + ['tag#1234', 'tag#5678', 'tag#4321']) + self.assertListEqual(list(to_list_view(self.triples_NM1, node=False, path=False, value=False)), + [123, '2010-01-02', 321, '2022-02-22']) + self.assertListEqual(list(to_list_view(self.triples_NMU, node=False, path=False, value=False)), + [123, 'tag#1234', 'tag#5678', 321, '2022-02-22']) + + +class TestDictView(unittest.TestCase): + def setUp(self): + self.unique_paths = {ns.bse.iso, ns.bse.t_created} + self.triples_111 = [('ent#1234', ns.bse.iso, 123)] + self.triples_11U = [('ent#1234', ns.bse.tag, 'tag#1234'), + ('ent#1234', ns.bse.tag, 'tag#5678')] + self.triples_1M1 = [('ent#1234', ns.bse.iso, 123), + ('ent#1234', ns.bse.t_created, '2010-01-02')] + self.triples_1MU = [('ent#1234', ns.bse.iso, 123), + ('ent#1234', ns.bse.tag, 'tag#1234'), + ('ent#1234', ns.bse.tag, 'tag#5678')] + self.triples_N11 = [('ent#1234', ns.bse.iso, 123), + ('ent#4321', ns.bse.iso, 321)] + self.triples_N1U = [('ent#1234', ns.bse.tag, 'tag#1234'), + ('ent#1234', ns.bse.tag, 'tag#5678'), + ('ent#4321', ns.bse.tag, 'tag#4321')] + self.triples_NM1 = [('ent#1234', ns.bse.iso, 123), + ('ent#1234', ns.bse.t_created, '2010-01-02'), + ('ent#4321', ns.bse.iso, 321), + ('ent#4321', ns.bse.t_created, '2022-02-22')] + self.triples_NMU = [('ent#1234', ns.bse.iso, 123), + ('ent#1234', ns.bse.tag, 'tag#1234'), + ('ent#1234', ns.bse.tag, 'tag#5678'), + ('ent#4321', ns.bse.iso, 321), + ('ent#4321', ns.bse.t_created, '2022-02-22')] + + def test_errounous_call(self): + # return set instead of value + self.assertSetEqual(to_dict_view(self.triples_111, one_node=False, one_path=True, unique_paths=self.unique_paths, node=False, path=False, value=False), + {123}) + self.assertSetEqual(to_dict_view(self.triples_111, one_node=True, one_path=False, unique_paths=self.unique_paths, node=False, path=False, value=False), + {123}) + # one_node mismatch: return set of values instead of value + self.assertDictEqual(to_dict_view(self.triples_111, one_node=False, one_path=True, unique_paths=self.unique_paths, node=False, path=True, value=False), + {ns.bse.iso: {123}}) + # one_path mismatch: return set of values instead of value + self.assertDictEqual(to_dict_view(self.triples_111, one_node=True, one_path=False, unique_paths=self.unique_paths, node=True, path=False, value=False), + {'ent#1234': {123}}) + # unique_paths mismatch: return set of values instead of value + self.assertSetEqual(to_dict_view(self.triples_111, one_node=True, one_path=False, unique_paths={}, node=False, path=False, value=False), + {123}) + self.assertDictEqual(to_dict_view(self.triples_111, one_node=True, one_path=False, unique_paths={}, node=False, path=True, value=False), + {ns.bse.iso: {123}}) + self.assertDictEqual(to_dict_view(self.triples_111, one_node=True, one_path=False, unique_paths={}, node=True, path=False, value=False), + {'ent#1234': {123}}) + self.assertDictEqual(to_dict_view(self.triples_111, one_node=True, one_path=False, unique_paths={}, node=True, path=True, value=False), + {'ent#1234': {ns.bse.iso: {123}}}) + + def test_agg_none(self): + self.assertDictEqual(to_dict_view(self.triples_111, one_node=True, one_path=True, unique_paths=self.unique_paths, node=True, path=True, value=True), + {'ent#1234': {ns.bse.iso: {123}}}) + self.assertDictEqual(to_dict_view(self.triples_11U, one_node=True, one_path=True, unique_paths=self.unique_paths, node=True, path=True, value=True), + {'ent#1234': {ns.bse.tag: {'tag#1234', 'tag#5678'}}}) + self.assertDictEqual(to_dict_view(self.triples_1M1, one_node=True, one_path=False, unique_paths=self.unique_paths, node=True, path=True, value=True), + {'ent#1234': {ns.bse.iso: {123}, ns.bse.t_created: {'2010-01-02'}}}) + self.assertDictEqual(to_dict_view(self.triples_1MU, one_node=True, one_path=False, unique_paths=self.unique_paths, node=True, path=True, value=True), + {'ent#1234': {ns.bse.iso: {123}, ns.bse.tag: {'tag#1234', 'tag#5678'}}}) + self.assertDictEqual(to_dict_view(self.triples_N11, one_node=False, one_path=True, unique_paths=self.unique_paths, node=True, path=True, value=True), + {'ent#1234': {ns.bse.iso: {123}}, 'ent#4321': {ns.bse.iso: {321}}}) + self.assertDictEqual(to_dict_view(self.triples_N1U, one_node=False, one_path=True, unique_paths=self.unique_paths, node=True, path=True, value=True), + {'ent#1234': {ns.bse.tag: {'tag#1234', 'tag#5678'}}, 'ent#4321': {ns.bse.tag: {'tag#4321'}}}) + self.assertDictEqual(to_dict_view(self.triples_NM1, one_node=False, one_path=False, unique_paths=self.unique_paths, node=True, path=True, value=True), + {'ent#1234': {ns.bse.iso: {123}, ns.bse.t_created: {'2010-01-02'}}, 'ent#4321': {ns.bse.iso: {321}, ns.bse.t_created: {'2022-02-22'}}}) + self.assertDictEqual(to_dict_view(self.triples_NMU, one_node=False, one_path=False, unique_paths=self.unique_paths, node=True, path=True, value=True), + {'ent#1234': {ns.bse.iso: {123}, ns.bse.tag: {'tag#1234', 'tag#5678'}}, 'ent#4321': {ns.bse.iso: {321}, ns.bse.t_created: {'2022-02-22'}}}) + # empty + self.assertDictEqual(to_dict_view([], one_node=True, one_path=True, unique_paths=self.unique_paths, node=True, path=True, value=True), {}) + self.assertDictEqual(to_dict_view([], one_node=True, one_path=False, unique_paths=self.unique_paths, node=True, path=True, value=True), {}) + self.assertDictEqual(to_dict_view([], one_node=False, one_path=True, unique_paths=self.unique_paths, node=True, path=True, value=True), {}) + self.assertDictEqual(to_dict_view([], one_node=False, one_path=False, unique_paths=self.unique_paths, node=True, path=True, value=True), {}) + + def test_agg_node(self): + self.assertDictEqual(to_dict_view(self.triples_111, one_node=True, one_path=True, unique_paths=self.unique_paths, node=False, path=True, value=True), + {ns.bse.iso: {123}}) + self.assertDictEqual(to_dict_view(self.triples_11U, one_node=True, one_path=True, unique_paths=self.unique_paths, node=False, path=True, value=True), + {ns.bse.tag: {'tag#1234', 'tag#5678'}}) + self.assertDictEqual(to_dict_view(self.triples_1M1, one_node=True, one_path=False, unique_paths=self.unique_paths, node=False, path=True, value=True), + {ns.bse.iso: {123}, ns.bse.t_created: {'2010-01-02'}}) + self.assertDictEqual(to_dict_view(self.triples_1MU, one_node=True, one_path=False, unique_paths=self.unique_paths, node=False, path=True, value=True), + {ns.bse.iso: {123}, ns.bse.tag: {'tag#1234', 'tag#5678'}}) + self.assertDictEqual(to_dict_view(self.triples_N11, one_node=False, one_path=True, unique_paths=self.unique_paths, node=False, path=True, value=True), + {ns.bse.iso: {123, 321}}) + self.assertDictEqual(to_dict_view(self.triples_N1U, one_node=False, one_path=True, unique_paths=self.unique_paths, node=False, path=True, value=True), + {ns.bse.tag: {'tag#1234', 'tag#5678', 'tag#4321'}}) + self.assertDictEqual(to_dict_view(self.triples_NM1, one_node=False, one_path=False, unique_paths=self.unique_paths, node=False, path=True, value=True), + {ns.bse.iso: {123, 321}, ns.bse.t_created: {'2010-01-02', '2022-02-22'}}) + self.assertDictEqual(to_dict_view(self.triples_NMU, one_node=False, one_path=False, unique_paths=self.unique_paths, node=False, path=True, value=True), + {ns.bse.iso: {123, 321}, ns.bse.tag: {'tag#1234', 'tag#5678'}, ns.bse.t_created: {'2022-02-22'}}) + # empty + self.assertDictEqual(to_dict_view([], one_node=True, one_path=True, unique_paths=self.unique_paths, node=False, path=True, value=True), {}) + self.assertDictEqual(to_dict_view([], one_node=True, one_path=False, unique_paths=self.unique_paths, node=False, path=True, value=True), {}) + self.assertDictEqual(to_dict_view([], one_node=False, one_path=True, unique_paths=self.unique_paths, node=False, path=True, value=True), {}) + self.assertDictEqual(to_dict_view([], one_node=False, one_path=False, unique_paths=self.unique_paths, node=False, path=True, value=True), {}) + + def test_agg_path(self): + self.assertDictEqual(to_dict_view(self.triples_111, one_node=True, one_path=True, unique_paths=self.unique_paths, node=True, path=False, value=True), + {'ent#1234': {123}}) + self.assertDictEqual(to_dict_view(self.triples_11U, one_node=True, one_path=True, unique_paths=self.unique_paths, node=True, path=False, value=True), + {'ent#1234': {'tag#1234', 'tag#5678'}}) + self.assertDictEqual(to_dict_view(self.triples_1M1, one_node=True, one_path=False, unique_paths=self.unique_paths, node=True, path=False, value=True), + {'ent#1234': {123, '2010-01-02'}}) + self.assertDictEqual(to_dict_view(self.triples_1MU, one_node=True, one_path=False, unique_paths=self.unique_paths, node=True, path=False, value=True), + {'ent#1234': {123, 'tag#1234', 'tag#5678'}}) + self.assertDictEqual(to_dict_view(self.triples_N11, one_node=False, one_path=True, unique_paths=self.unique_paths, node=True, path=False, value=True), + {'ent#1234': {123}, 'ent#4321': {321}}) + self.assertDictEqual(to_dict_view(self.triples_N1U, one_node=False, one_path=True, unique_paths=self.unique_paths, node=True, path=False, value=True), + {'ent#1234': {'tag#1234', 'tag#5678'}, 'ent#4321': {'tag#4321'}}) + self.assertDictEqual(to_dict_view(self.triples_NM1, one_node=False, one_path=False, unique_paths=self.unique_paths, node=True, path=False, value=True), + {'ent#1234': {123, '2010-01-02'}, 'ent#4321': {321, '2022-02-22'}}) + self.assertDictEqual(to_dict_view(self.triples_NMU, one_node=False, one_path=False, unique_paths=self.unique_paths, node=True, path=False, value=True), + {'ent#1234': {123, 'tag#1234', 'tag#5678'}, 'ent#4321': {321, '2022-02-22'}}) + # empty + self.assertDictEqual(to_dict_view([], one_node=True, one_path=True, unique_paths=self.unique_paths, node=True, path=False, value=True), {}) + self.assertDictEqual(to_dict_view([], one_node=True, one_path=False, unique_paths=self.unique_paths, node=True, path=False, value=True), {}) + self.assertDictEqual(to_dict_view([], one_node=False, one_path=True, unique_paths=self.unique_paths, node=True, path=False, value=True), {}) + self.assertDictEqual(to_dict_view([], one_node=False, one_path=False, unique_paths=self.unique_paths, node=True, path=False, value=True), {}) + + def test_agg_node_path(self): + self.assertSetEqual(to_dict_view(self.triples_111, one_node=True, one_path=True, unique_paths=self.unique_paths, node=False, path=False, value=True), + {123}) + self.assertSetEqual(to_dict_view(self.triples_11U, one_node=True, one_path=True, unique_paths=self.unique_paths, node=False, path=False, value=True), + {'tag#1234', 'tag#5678'}) + self.assertSetEqual(to_dict_view(self.triples_1M1, one_node=True, one_path=False, unique_paths=self.unique_paths, node=False, path=False, value=True), + {123, '2010-01-02'}) + self.assertSetEqual(to_dict_view(self.triples_1MU, one_node=True, one_path=False, unique_paths=self.unique_paths, node=False, path=False, value=True), + {123, 'tag#1234', 'tag#5678'}) + self.assertSetEqual(to_dict_view(self.triples_N11, one_node=False, one_path=True, unique_paths=self.unique_paths, node=False, path=False, value=True), + {123, 321}) + self.assertSetEqual(to_dict_view(self.triples_N1U, one_node=False, one_path=True, unique_paths=self.unique_paths, node=False, path=False, value=True), + {'tag#1234', 'tag#5678', 'tag#4321'}) + self.assertSetEqual(to_dict_view(self.triples_NM1, one_node=False, one_path=False, unique_paths=self.unique_paths, node=False, path=False, value=True), + {123, '2010-01-02', 321, '2022-02-22'}) + self.assertSetEqual(to_dict_view(self.triples_NMU, one_node=False, one_path=False, unique_paths=self.unique_paths, node=False, path=False, value=True), + {123, 'tag#1234', 'tag#5678', 321, '2022-02-22'}) + # empty + self.assertSetEqual(to_dict_view([], one_node=True, one_path=True, unique_paths=self.unique_paths, node=False, path=False, value=True), set()) + self.assertSetEqual(to_dict_view([], one_node=True, one_path=False, unique_paths=self.unique_paths, node=False, path=False, value=True), set()) + self.assertSetEqual(to_dict_view([], one_node=False, one_path=True, unique_paths=self.unique_paths, node=False, path=False, value=True), set()) + self.assertSetEqual(to_dict_view([], one_node=False, one_path=False, unique_paths=self.unique_paths, node=False, path=False, value=True), set()) + + def test_agg_value(self): + self.assertDictEqual(to_dict_view(self.triples_111, one_node=True, one_path=True, unique_paths=self.unique_paths, node=True, path=True, value=False), + {'ent#1234': {ns.bse.iso: 123}}) + self.assertDictEqual(to_dict_view(self.triples_11U, one_node=True, one_path=True, unique_paths=self.unique_paths, node=True, path=True, value=False), + {'ent#1234': {ns.bse.tag: {'tag#1234', 'tag#5678'}}}) + self.assertDictEqual(to_dict_view(self.triples_1M1, one_node=True, one_path=False, unique_paths=self.unique_paths, node=True, path=True, value=False), + {'ent#1234': {ns.bse.iso: 123, ns.bse.t_created: '2010-01-02'}}) + self.assertDictEqual(to_dict_view(self.triples_1MU, one_node=True, one_path=False, unique_paths=self.unique_paths, node=True, path=True, value=False), + {'ent#1234': {ns.bse.iso: 123, ns.bse.tag: {'tag#1234', 'tag#5678'}}}) + self.assertDictEqual(to_dict_view(self.triples_N11, one_node=False, one_path=True, unique_paths=self.unique_paths, node=True, path=True, value=False), + {'ent#1234': {ns.bse.iso: 123}, 'ent#4321': {ns.bse.iso: 321}}) + self.assertDictEqual(to_dict_view(self.triples_N1U, one_node=False, one_path=True, unique_paths=self.unique_paths, node=True, path=True, value=False), + {'ent#1234': {ns.bse.tag: {'tag#1234', 'tag#5678'}}, 'ent#4321': {ns.bse.tag: {'tag#4321'}}}) + self.assertDictEqual(to_dict_view(self.triples_NM1, one_node=False, one_path=False, unique_paths=self.unique_paths, node=True, path=True, value=False), + {'ent#1234': {ns.bse.iso: 123, ns.bse.t_created: '2010-01-02'}, 'ent#4321': {ns.bse.iso: 321, ns.bse.t_created: '2022-02-22'}}) + self.assertDictEqual(to_dict_view(self.triples_NMU, one_node=False, one_path=False, unique_paths=self.unique_paths, node=True, path=True, value=False), + {'ent#1234': {ns.bse.iso: 123, ns.bse.tag: {'tag#1234', 'tag#5678'}}, 'ent#4321': {ns.bse.iso: 321, ns.bse.t_created: '2022-02-22'}}) + # empty + self.assertDictEqual(to_dict_view([], one_node=True, one_path=True, unique_paths=self.unique_paths, node=True, path=True, value=False), {}) + self.assertDictEqual(to_dict_view([], one_node=True, one_path=False, unique_paths=self.unique_paths, node=True, path=True, value=False), {}) + self.assertDictEqual(to_dict_view([], one_node=False, one_path=True, unique_paths=self.unique_paths, node=True, path=True, value=False), {}) + self.assertDictEqual(to_dict_view([], one_node=False, one_path=False, unique_paths=self.unique_paths, node=True, path=True, value=False), {}) + + def test_agg_node_value(self): + self.assertDictEqual(to_dict_view(self.triples_111, one_node=True, one_path=True, unique_paths=self.unique_paths, node=False, path=True, value=False), + {ns.bse.iso: 123}) + self.assertDictEqual(to_dict_view(self.triples_11U, one_node=True, one_path=True, unique_paths=self.unique_paths, node=False, path=True, value=False), + {ns.bse.tag: {'tag#1234', 'tag#5678'}}) + self.assertDictEqual(to_dict_view(self.triples_1M1, one_node=True, one_path=False, unique_paths=self.unique_paths, node=False, path=True, value=False), + {ns.bse.iso: 123, ns.bse.t_created: '2010-01-02'}) + self.assertDictEqual(to_dict_view(self.triples_1MU, one_node=True, one_path=False, unique_paths=self.unique_paths, node=False, path=True, value=False), + {ns.bse.iso: 123, ns.bse.tag: {'tag#1234', 'tag#5678'}}) + self.assertDictEqual(to_dict_view(self.triples_N11, one_node=False, one_path=True, unique_paths=self.unique_paths, node=False, path=True, value=False), + {ns.bse.iso: {123, 321}}) + self.assertDictEqual(to_dict_view(self.triples_N1U, one_node=False, one_path=True, unique_paths=self.unique_paths, node=False, path=True, value=False), + {ns.bse.tag: {'tag#1234', 'tag#5678', 'tag#4321'}}) + self.assertDictEqual(to_dict_view(self.triples_NM1, one_node=False, one_path=False, unique_paths=self.unique_paths, node=False, path=True, value=False), + {ns.bse.iso: {123, 321}, ns.bse.t_created: {'2010-01-02', '2022-02-22'}}) + self.assertDictEqual(to_dict_view(self.triples_NMU, one_node=False, one_path=False, unique_paths=self.unique_paths, node=False, path=True, value=False), + {ns.bse.iso: {123, 321}, ns.bse.tag: {'tag#1234', 'tag#5678'}, ns.bse.t_created: {'2022-02-22'}}) + # empty + self.assertDictEqual(to_dict_view([], one_node=True, one_path=True, unique_paths=self.unique_paths, node=False, path=True, value=False), {}) + self.assertDictEqual(to_dict_view([], one_node=True, one_path=False, unique_paths=self.unique_paths, node=False, path=True, value=False), {}) + self.assertDictEqual(to_dict_view([], one_node=False, one_path=True, unique_paths=self.unique_paths, node=False, path=True, value=False), {}) + self.assertDictEqual(to_dict_view([], one_node=False, one_path=False, unique_paths=self.unique_paths, node=False, path=True, value=False), {}) + + def test_agg_path_value(self): + self.assertDictEqual(to_dict_view(self.triples_111, one_node=True, one_path=True, unique_paths=self.unique_paths, node=True, path=False, value=False), + {'ent#1234': 123}) + self.assertDictEqual(to_dict_view(self.triples_11U, one_node=True, one_path=True, unique_paths=self.unique_paths, node=True, path=False, value=False), + {'ent#1234': {'tag#1234', 'tag#5678'}}) + self.assertDictEqual(to_dict_view(self.triples_1M1, one_node=True, one_path=False, unique_paths=self.unique_paths, node=True, path=False, value=False), + {'ent#1234': {123, '2010-01-02'}}) + self.assertDictEqual(to_dict_view(self.triples_1MU, one_node=True, one_path=False, unique_paths=self.unique_paths, node=True, path=False, value=False), + {'ent#1234': {123, 'tag#1234', 'tag#5678'}}) + self.assertDictEqual(to_dict_view(self.triples_N11, one_node=False, one_path=True, unique_paths=self.unique_paths, node=True, path=False, value=False), + {'ent#1234': 123, 'ent#4321': 321}) + self.assertDictEqual(to_dict_view(self.triples_N1U, one_node=False, one_path=True, unique_paths=self.unique_paths, node=True, path=False, value=False), + {'ent#1234': {'tag#1234', 'tag#5678'}, 'ent#4321': {'tag#4321'}}) + self.assertDictEqual(to_dict_view(self.triples_NM1, one_node=False, one_path=False, unique_paths=self.unique_paths, node=True, path=False, value=False), + {'ent#1234': {123, '2010-01-02'}, 'ent#4321': {321, '2022-02-22'}}) + self.assertDictEqual(to_dict_view(self.triples_NMU, one_node=False, one_path=False, unique_paths=self.unique_paths, node=True, path=False, value=False), + {'ent#1234': {123, 'tag#1234', 'tag#5678'}, 'ent#4321': {321, '2022-02-22'}}) + # empty + self.assertDictEqual(to_dict_view([], one_node=True, one_path=True, unique_paths=self.unique_paths, node=True, path=False, value=False), {}) + self.assertDictEqual(to_dict_view([], one_node=True, one_path=False, unique_paths=self.unique_paths, node=True, path=False, value=False), {}) + self.assertDictEqual(to_dict_view([], one_node=False, one_path=True, unique_paths=self.unique_paths, node=True, path=False, value=False), {}) + self.assertDictEqual(to_dict_view([], one_node=False, one_path=False, unique_paths=self.unique_paths, node=True, path=False, value=False), {}) + + def test_agg_all(self): + self.assertEqual(to_dict_view(self.triples_111, one_node=True, one_path=True, unique_paths=self.unique_paths, node=False, path=False, value=False), + 123) + self.assertSetEqual(to_dict_view(self.triples_11U, one_node=True, one_path=True, unique_paths=self.unique_paths, node=False, path=False, value=False), + {'tag#1234', 'tag#5678'}) + self.assertSetEqual(to_dict_view(self.triples_1M1, one_node=True, one_path=False, unique_paths=self.unique_paths, node=False, path=False, value=False), + {123, '2010-01-02'}) + self.assertSetEqual(to_dict_view(self.triples_1MU, one_node=True, one_path=False, unique_paths=self.unique_paths, node=False, path=False, value=False), + {123, 'tag#1234', 'tag#5678'}) + self.assertSetEqual(to_dict_view(self.triples_N11, one_node=False, one_path=True, unique_paths=self.unique_paths, node=False, path=False, value=False), + {123, 321}) + self.assertSetEqual(to_dict_view(self.triples_N1U, one_node=False, one_path=True, unique_paths=self.unique_paths, node=False, path=False, value=False), + {'tag#1234', 'tag#5678', 'tag#4321'}) + self.assertSetEqual(to_dict_view(self.triples_NM1, one_node=False, one_path=False, unique_paths=self.unique_paths, node=False, path=False, value=False), + {123, '2010-01-02', 321, '2022-02-22'}) + self.assertSetEqual(to_dict_view(self.triples_NMU, one_node=False, one_path=False, unique_paths=self.unique_paths, node=False, path=False, value=False), + {123, 'tag#1234', 'tag#5678', 321, '2022-02-22'}) + # empty + self.assertEqual(to_dict_view([], one_node=True, one_path=True, unique_paths=self.unique_paths, node=False, path=False, value=False), None) + self.assertSetEqual(to_dict_view([], one_node=True, one_path=False, unique_paths=self.unique_paths, node=False, path=False, value=False), set()) + self.assertSetEqual(to_dict_view([], one_node=False, one_path=True, unique_paths=self.unique_paths, node=False, path=False, value=False), set()) + self.assertSetEqual(to_dict_view([], one_node=False, one_path=False, unique_paths=self.unique_paths, node=False, path=False, value=False), set()) + self.assertEqual(to_dict_view([], one_node=True, one_path=True, unique_paths=self.unique_paths, node=False, path=False, value=False, default=123), 123) + +## main ## + +if __name__ == '__main__': + unittest.main() + +## EOF ## diff --git a/test/graph/test_walk.py b/test/graph/test_walk.py new file mode 100644 index 0000000..4b844da --- /dev/null +++ b/test/graph/test_walk.py @@ -0,0 +1,170 @@ + +# imports +import unittest + +# bsfs imports +from bsfs import schema as bsc +from bsfs.graph import Graph +from bsfs.graph.ac import NullAC +from bsfs.namespace import Namespace, ns +from bsfs.triple_store.sparql import SparqlStore +from bsfs.utils import URI + +# symbol to test +from bsfs.graph.walk import Walk + +## code ## + +ns.bse = ns.bsfs.Entity() +ns.bst = ns.bsfs.Tag() + +class TestWalk(unittest.TestCase): + def setUp(self): + # backend setup + self.schema = bsc.from_string(''' + prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> + prefix xsd: <http://www.w3.org/2001/XMLSchema#> + prefix bsfs: <https://schema.bsfs.io/core/> + prefix bse: <https://schema.bsfs.io/core/Entity#> + prefix bst: <https://schema.bsfs.io/core/Tag#> + + bsfs:Entity rdfs:subClassOf bsfs:Node . + bsfs:Tag rdfs:subClassOf bsfs:Node . + bsfs:User rdfs:subClassOf bsfs:Node . + xsd:string rdfs:subClassOf bsfs:Literal . + + bse:author rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Node ; + rdfs:range bsfs:User . + + bse:tag rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity ; + rdfs:range bsfs:Tag . + + bst:label rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Tag ; + rdfs:range xsd:string ; + bsfs:unique "true"^^xsd:boolean . + + bst:subTagOf rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Tag ; + rdfs:range bsfs:Tag . + + bst:main rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Tag ; + rdfs:range bsfs:Entity . + + bst:author rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Tag ; + rdfs:range xsd:string . + + ''') + self.backend = SparqlStore.Open() + self.user = URI('http://example.com/me') + self.ac = NullAC(self.backend, self.user) + self.graph = Graph(self.backend, self.ac) + self.graph.migrate(self.schema) + + # nodes setup + self.ents = self.graph.nodes(ns.bsfs.Entity, { + URI('http://example.com/me/entity#1234'), + URI('http://example.com/me/entity#4321')}) + self.tags = self.graph.nodes(ns.bsfs.Tag, { + URI('http://example.com/me/tag#1234'), + URI('http://example.com/me/tag#4321')}) + # add some instances + self.ents.set(ns.bse.tag, self.tags) + self.graph.node(ns.bsfs.Tag, URI('http://example.com/me/tag#1234')).set(ns.bst.label, 'hello') + self.graph.node(ns.bsfs.Tag, URI('http://example.com/me/tag#4321')).set(ns.bst.label, 'world') + + def test_essentials(self): # __eq__, __hash__, __str__, __repr__ + p_author = self.schema.predicate(ns.bse.author) + p_tag = self.schema.predicate(ns.bse.tag) + p_main = self.schema.predicate(ns.bst.main) + # comparison + self.assertEqual(Walk(self.ents, [p_tag]), Walk(self.ents, [p_tag])) + self.assertEqual(hash(Walk(self.ents, [p_tag])), hash(Walk(self.ents, [p_tag]))) + # comparison respects type + class Foo(Walk): pass + self.assertNotEqual(Walk(self.ents, [p_tag]), Foo(self.ents, [p_tag])) + self.assertNotEqual(hash(Walk(self.ents, [p_tag])), hash(Foo(self.ents, [p_tag]))) + # comparison respects root + self.assertNotEqual(Walk(self.ents, [p_author]), Walk(self.tags, [p_author])) + self.assertNotEqual(hash(Walk(self.ents, [p_author])), hash(Walk(self.tags, [p_author]))) + # comparison respects path + self.assertNotEqual(Walk(self.tags, [p_author]), Walk(self.tags, [p_main])) + self.assertNotEqual(hash(Walk(self.tags, [p_author])), hash(Walk(self.tags, [p_main]))) + # string conversion + self.assertEqual(str(Walk(self.ents, [p_tag, p_main])), + 'Walk(@https://schema.bsfs.io/core/Entity: https://schema.bsfs.io/core/Entity#tag, https://schema.bsfs.io/core/Tag#main)') + self.assertEqual(repr(Walk(self.ents, [p_tag, p_main])), + 'Walk(https://schema.bsfs.io/core/Entity, (https://schema.bsfs.io/core/Entity#tag, https://schema.bsfs.io/core/Tag#main))') + + def test_tail(self): + self.assertEqual(Walk(self.ents, ( + self.schema.predicate(ns.bse.tag), + )).tail, + self.schema.node(ns.bsfs.Tag)) + self.assertEqual(Walk(self.ents, ( + self.schema.predicate(ns.bse.tag), + self.schema.predicate(ns.bst.main), + )).tail, + self.schema.node(ns.bsfs.Entity)) + + def test_step(self): + tag_type = self.schema.node(ns.bsfs.Tag) + # step returns a predicate + self.assertEqual(Walk.step(self.schema, tag_type, 'subTagOf'), + (self.schema.predicate(ns.bst.subTagOf), )) + # invalid step raises an error + self.assertRaises(ValueError, Walk.step, self.schema, tag_type, 'foobar') + # ambiguous step raises an error + self.assertRaises(ValueError, Walk.step, self.schema, tag_type, 'author') + + def test_getattr(self): # __getattr__ + walk = Walk(self.ents, (self.schema.predicate(ns.bse.tag), )) + # first step + self.assertEqual(walk.subTagOf, Walk(self.ents, ( + self.schema.predicate(ns.bse.tag), + self.schema.predicate(ns.bst.subTagOf), + ))) + # second step + self.assertEqual(walk.subTagOf.main, Walk(self.ents, ( + self.schema.predicate(ns.bse.tag), + self.schema.predicate(ns.bst.subTagOf), + self.schema.predicate(ns.bst.main), + ))) + # invalid step raises an error + self.assertRaises(ValueError, getattr, walk, 'foobar') + # ambiguous step raises an error + self.assertRaises(ValueError, getattr, walk, 'author') + + def test_get(self): # get, __call__ + walk = Walk(self.ents, (self.schema.predicate(ns.bse.tag), )) + tags = { + self.graph.node(ns.bsfs.Tag, URI('http://example.com/me/tag#1234')), + self.graph.node(ns.bsfs.Tag, URI('http://example.com/me/tag#4321'))} + # get returns from Nodes.get + self.assertDictEqual(walk.get(), { + self.graph.node(ns.bsfs.Entity, URI('http://example.com/me/entity#1234')): tags, + self.graph.node(ns.bsfs.Entity, URI('http://example.com/me/entity#4321')): tags, + }) + self.assertDictEqual(walk(), { + self.graph.node(ns.bsfs.Entity, URI('http://example.com/me/entity#1234')): tags, + self.graph.node(ns.bsfs.Entity, URI('http://example.com/me/entity#4321')): tags, + }) + # get passes kwargs to Nodes.get + self.assertSetEqual(tags, walk.get(node=False)) + self.assertSetEqual(tags, walk(node=False)) + self.assertSetEqual(tags, set(walk.get(view=list, node=False))) + self.assertSetEqual(tags, set(walk(view=list, node=False))) + # get returns values if need be + self.assertSetEqual(walk.label(node=False), {'hello', 'world'}) + + +## main ## + +if __name__ == '__main__': + unittest.main() + +## EOF ## |