From bbfcee4fffc553b5dd08f37a79dd6ccddbf340f8 Mon Sep 17 00:00:00 2001 From: Matthias Baumgartner Date: Thu, 8 Dec 2022 16:32:52 +0100 Subject: uri and some utils --- test/__init__.py | 0 test/utils/__init__.py | 0 test/utils/test_commons.py | 31 ++++++++ test/utils/test_uri.py | 171 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 202 insertions(+) create mode 100644 test/__init__.py create mode 100644 test/utils/__init__.py create mode 100644 test/utils/test_commons.py create mode 100644 test/utils/test_uri.py (limited to 'test') diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/utils/__init__.py b/test/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/utils/test_commons.py b/test/utils/test_commons.py new file mode 100644 index 0000000..ce73788 --- /dev/null +++ b/test/utils/test_commons.py @@ -0,0 +1,31 @@ +""" + +Part of the tagit test suite. +A copy of the license is provided with the project. +Author: Matthias Baumgartner, 2022 +""" +# imports +import unittest + +# objects to test +from bsfs.utils.commons import typename + + +## code ## + +class TestCommons(unittest.TestCase): + def test_typename(self): + class Foo(): pass + self.assertEqual(typename(Foo()), 'Foo') + self.assertEqual(typename('hello'), 'str') + self.assertEqual(typename(123), 'int') + self.assertEqual(typename(None), 'NoneType') + + + +## main ## + +if __name__ == '__main__': + unittest.main() + +## EOF ## diff --git a/test/utils/test_uri.py b/test/utils/test_uri.py new file mode 100644 index 0000000..976e75d --- /dev/null +++ b/test/utils/test_uri.py @@ -0,0 +1,171 @@ +""" + +Part of the tagit test suite. +A copy of the license is provided with the project. +Author: Matthias Baumgartner, 2022 +""" +# imports +import unittest + +# objects to test +from bsfs.utils.uri import URI + + +## code ## + +class TestURI(unittest.TestCase): + + def test_new(self): + # cannot create an unparseable URI + self.assertRaises(ValueError, URI, 'http://') + # returns URI otherwise + self.assertIsInstance(URI('http://user@www.example.com:1234/path0/path1?query#fragment'), URI) + + def test_is_parseable(self): + # empty string is a parseable uri + self.assertTrue(URI.is_parseable('')) + # examples from the RFC are parseable + self.assertTrue(URI.is_parseable('foo://example.com:8042/over/there?name=ferret#nose')) + self.assertTrue(URI.is_parseable('urn:example:animal:ferret:nose')) + self.assertTrue(URI.is_parseable('mailto:fred@xample.com')) + self.assertTrue(URI.is_parseable('www.w3.org/Addressing/')) + self.assertTrue(URI.is_parseable('ftp://cnn.example.com&store=breaking_news@10.0.0.1/top_story.htm')) + self.assertTrue(URI.is_parseable('ftp://ftp.is.co.za/rfc/rfc1808.txt')) + self.assertTrue(URI.is_parseable('http://www.ietf.org/rfc/rfc2396.txt')) + self.assertTrue(URI.is_parseable('ldap://[2001:db8::7]/c=GB?objectClass?one')) + self.assertTrue(URI.is_parseable('mailto:John.Doe@example.com')) + self.assertTrue(URI.is_parseable('news:comp.infosystems.www.servers.unix')) + self.assertTrue(URI.is_parseable('tel:+1-816-555-1212')) + self.assertTrue(URI.is_parseable('telnet://192.0.2.16:80/')) + self.assertTrue(URI.is_parseable('urn:oasis:names:specification:docbook:dtd:xml:4.1.2')) + + # uri cannot end with a scheme delimiter + self.assertFalse(URI.is_parseable('http://')) + # port must be a number + self.assertFalse(URI.is_parseable('http://example.com:foo/')) + # the double slash (//) implies a authority + self.assertFalse(URI.is_parseable('http:///path0/path1?query#fragment')) + + def test_compose(self): + self.assertEqual(URI.compose('path'), '/path') + self.assertEqual(URI.compose('/path'), '/path') # leading slash is not repeated + self.assertEqual(URI.compose('path', scheme='scheme'), 'scheme:/path') + self.assertEqual(URI.compose('path', authority='authority'), '//authority/path') + self.assertEqual(URI.compose('path', host='host'), '//host/path') + self.assertEqual(URI.compose('path', user='user'), '/path') # user w/o host is ignored + self.assertEqual(URI.compose('path', host='host', user='user'), '//user@host/path') + self.assertEqual(URI.compose('path', port='port'), '/path') # port w/o host is ignored + self.assertEqual(URI.compose('path', host='host', port=1234), '//host:1234/path') + self.assertEqual(URI.compose('path', host='host', port='1234'), '//host:1234/path') + self.assertRaises(ValueError, URI.compose, 'path', host='host', port='foo') # port must be a number + self.assertEqual(URI.compose('path', host='host', user='foo', port='1234'), '//foo@host:1234/path') + self.assertEqual(URI.compose('path', query='query'), '/path?query') + self.assertEqual(URI.compose('path', fragment='fragment'), '/path#fragment') + self.assertEqual(URI.compose('path', 'scheme', 'authority', 'user', 'host', 1234, 'query', 'fragment'), + 'scheme://user@host:1234/path?query#fragment') + + def test_get(self): + # get returns the respective component + self.assertEqual(URI('http://user@www.example.com:1234/path0/path1?query#fragment').get('scheme'), 'http') + self.assertEqual(URI('http://user@www.example.com:1234/path0/path1?query#fragment').get('authority'), 'user@www.example.com:1234') + self.assertEqual(URI('http://user@www.example.com:1234/path0/path1?query#fragment').get('userinfo'), 'user') + self.assertEqual(URI('http://user@www.example.com:1234/path0/path1?query#fragment').get('host'), 'www.example.com') + self.assertEqual(URI('http://user@www.example.com:1234/path0/path1?query#fragment').get('port'), 1234) + self.assertEqual(URI('http://user@www.example.com:1234/path0/path1?query#fragment').get('path'), '/path0/path1') + self.assertEqual(URI('http://user@www.example.com:1234/path0/path1?query#fragment').get('query'), 'query') + self.assertEqual(URI('http://user@www.example.com:1234/path0/path1?query#fragment').get('fragment'), 'fragment') + # get returns a default value if the component is missing + class Foo(): pass + foo = Foo() + self.assertEqual(URI('//user@www.example.com:1234/path0/path1?query#fragment').get('scheme', foo), foo) + self.assertEqual(URI('/path0/path1?query#fragment').get('authority', foo), foo) + self.assertEqual(URI('http://www.example.com:1234/path0/path1?query#fragment').get('userinfo', foo), foo) + self.assertEqual(URI('/path0/path1?query#fragment').get('host', foo), foo) + self.assertEqual(URI('http://user@www.example.com/path0/path1?query#fragment').get('port', foo), foo) + self.assertEqual(URI('http://user@www.example.com:1234/path0/path1#fragment').get('query', foo), foo) + self.assertEqual(URI('http://user@www.example.com:1234/path0/path1?query').get('fragment', foo), foo) + # can only get components + self.assertRaises(ValueError, URI('').get, 'foobar') + + def test_scheme(self): + # full URI + self.assertEqual(URI('http://user@www.example.com:1234/path0/path1?query#fragment').scheme, 'http') + self.assertEqual(URI('ftp://user@www.example.com:1234/path0/path1?query#fragment').scheme, 'ftp') + self.assertEqual(URI('myown://user@www.example.com:1234/path0/path1?query#fragment').scheme, 'myown') + # empty scheme + self.assertRaises(ValueError, getattr, URI('www.example.com/path0/path1?query#fragment'), 'scheme') + # empty URI + self.assertRaises(ValueError, getattr, URI(''), 'scheme') + + def test_authority(self): + # full URI + self.assertEqual(URI('http://user@www.example.com:1234/path0/path1?query#fragment').authority, 'user@www.example.com:1234') + # empty authority + self.assertRaises(ValueError, getattr, URI('http/path0/path1?query#fragment'), 'authority') + # empty URI + self.assertRaises(ValueError, getattr, URI(''), 'authority') + + def test_userinfo(self): + # full URI + self.assertEqual(URI('http://user@www.example.com:1234/path0/path1?query#fragment').scheme, 'http') + # empty authority + self.assertRaises(ValueError, getattr, URI('http/path0/path1?query#fragment'), 'userinfo') + # empty userinfo + self.assertRaises(ValueError, getattr, URI('http://www.example.com:1234/path0/path1?query#fragment'), 'userinfo') + # empty URI + self.assertRaises(ValueError, getattr, URI(''), 'userinfo') + + def test_host(self): + # full URI + self.assertEqual(URI('http://user@www.example.com:1234/path0/path1?query#fragment').host, 'www.example.com') + # IPv4 host + self.assertEqual(URI('http://user@10.0.0.1:1234/path0/path1?query#fragment').host, '10.0.0.1') + # IPv6 host + self.assertEqual(URI('http://user@[::64]:1234/path0/path1?query#fragment').host, '[::64]') + # empty authority + self.assertRaises(ValueError, getattr, URI('http/path0/path1?query#fragment'), 'host') + # empty URI + self.assertRaises(ValueError, getattr, URI(''), 'host') + + def test_port(self): + # full URI + self.assertEqual(URI('http://user@www.example.com:1234/path0/path1?query#fragment').port, 1234) + # empty authority + self.assertRaises(ValueError, getattr, URI('http/path0/path1?query#fragment'), 'port') + # empty port + self.assertRaises(ValueError, getattr, URI('http://user@www.example.com/path0/path1?query#fragment'), 'port') + # empty URI + self.assertRaises(ValueError, getattr, URI(''), 'port') + + def test_path(self): + # full URI + self.assertEqual(URI('http://user@www.example.com:1234/path0/path1?query#fragment').path, '/path0/path1') + # empty path + self.assertEqual(URI('http://user@www.example.com:1234?query#fragment').path, '') + # empty URI + self.assertEqual(URI('').path, '') + + def test_query(self): + # full URI + self.assertEqual(URI('http://user@www.example.com:1234/path0/path1?query#fragment').query, 'query') + # empty query + self.assertRaises(ValueError, getattr, URI('http://user@www.example.com:1234/path0/path1#fragment'), 'query') + # empty URI + self.assertRaises(ValueError, getattr, URI(''), 'query') + + def test_fragment(self): + # full URI + self.assertEqual(URI('http://user@www.example.com:1234/path0/path1?query#fragment').fragment, 'fragment') + # empty fragment + self.assertRaises(ValueError, getattr, URI('http://user@www.example.com:1234/path0/path1?query'), 'fragment') + # empty URI + self.assertRaises(ValueError, getattr, URI(''), 'fragment') + + + +## main ## + +if __name__ == '__main__': + unittest.main() + +## EOF ## -- cgit v1.2.3 From 729f025f392d45b621941da9d052834e0d81506e Mon Sep 17 00:00:00 2001 From: Matthias Baumgartner Date: Thu, 8 Dec 2022 16:33:36 +0100 Subject: namespaces --- test/namespace/__init__.py | 0 test/namespace/test_namespace.py | 97 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 test/namespace/__init__.py create mode 100644 test/namespace/test_namespace.py (limited to 'test') diff --git a/test/namespace/__init__.py b/test/namespace/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/namespace/test_namespace.py b/test/namespace/test_namespace.py new file mode 100644 index 0000000..1ad53e3 --- /dev/null +++ b/test/namespace/test_namespace.py @@ -0,0 +1,97 @@ +""" + +Part of the tagit test suite. +A copy of the license is provided with the project. +Author: Matthias Baumgartner, 2022 +""" +# imports +import unittest + +# objects to test +from bsfs.namespace.namespace import Namespace, ClosedNamespace + + +## code ## + +class TestNamespace(unittest.TestCase): + def test_essentials(self): + # string conversion + self.assertEqual(str(Namespace('http://example.org/')), 'Namespace(http://example.org/)') + self.assertEqual(str(Namespace('http://example.org#')), 'Namespace(http://example.org#)') + self.assertEqual(repr(Namespace('http://example.org/')), 'Namespace(http://example.org/)') + self.assertEqual(repr(Namespace('http://example.org#')), 'Namespace(http://example.org#)') + # comparison + class Foo(Namespace): pass + self.assertEqual(Namespace('http://example.org/'), Namespace('http://example.org/')) + self.assertNotEqual(Namespace('http://example.org/'), Foo('http://example.org/')) + self.assertNotEqual(Foo('http://example.org/'), Namespace('http://example.org/')) + self.assertNotEqual(Namespace('http://example.org/'), Namespace('http://example.org#')) + self.assertNotEqual(Namespace('http://example.org#'), Namespace('http://example.org/')) + # hashing + self.assertEqual(hash(Namespace('http://example.org/')), hash(Namespace('http://example.org/'))) + self.assertNotEqual(hash(Namespace('http://example.org/')), hash(Foo('http://example.org/'))) + self.assertNotEqual(hash(Foo('http://example.org/')), hash(Namespace('http://example.org/'))) + self.assertNotEqual(hash(Namespace('http://example.org/')), hash(Namespace('http://example.org#'))) + self.assertNotEqual(hash(Namespace('http://example.org#')), hash(Namespace('http://example.org/'))) + + def test_getattr(self): + self.assertEqual(Namespace('http://example.org/').foo, 'http://example.org/foo') + self.assertEqual(Namespace('http://example.org/').bar, 'http://example.org/bar') + self.assertEqual(Namespace('http://example.org/me#').foo, 'http://example.org/me#foo') + self.assertEqual(Namespace('http://example.org/me#').bar, 'http://example.org/me#bar') + + def test_getitem(self): + self.assertEqual(Namespace('http://example.org/')['foo'], 'http://example.org/foo') + self.assertEqual(Namespace('http://example.org/')['bar'], 'http://example.org/bar') + self.assertEqual(Namespace('http://example.org/me#')['foo'], 'http://example.org/me#foo') + self.assertEqual(Namespace('http://example.org/me#')['bar'], 'http://example.org/me#bar') + + +class TestClosedNamespace(unittest.TestCase): + def test_essentials(self): + # string conversion + self.assertEqual(str(ClosedNamespace('http://example.org/')), 'ClosedNamespace(http://example.org/)') + self.assertEqual(str(ClosedNamespace('http://example.org#')), 'ClosedNamespace(http://example.org#)') + self.assertEqual(repr(ClosedNamespace('http://example.org/')), 'ClosedNamespace(http://example.org/)') + self.assertEqual(repr(ClosedNamespace('http://example.org#')), 'ClosedNamespace(http://example.org#)') + # comparison + class Foo(ClosedNamespace): pass + self.assertEqual(ClosedNamespace('http://example.org/'), ClosedNamespace('http://example.org/')) + self.assertEqual(ClosedNamespace('http://example.org/', 'foo', 'bar'), ClosedNamespace('http://example.org/', 'foo', 'bar')) + self.assertNotEqual(ClosedNamespace('http://example.org/', 'foo'), ClosedNamespace('http://example.org/', 'bar')) + self.assertNotEqual(ClosedNamespace('http://example.org/'), Foo('http://example.org/')) + self.assertNotEqual(Foo('http://example.org/'), ClosedNamespace('http://example.org/')) + self.assertNotEqual(ClosedNamespace('http://example.org/'), ClosedNamespace('http://example.org#')) + self.assertNotEqual(ClosedNamespace('http://example.org#'), ClosedNamespace('http://example.org/')) + # hashing + self.assertEqual(hash(ClosedNamespace('http://example.org/')), hash(ClosedNamespace('http://example.org/'))) + self.assertEqual(hash(ClosedNamespace('http://example.org/', 'foo', 'bar')), hash(ClosedNamespace('http://example.org/', 'foo', 'bar'))) + self.assertNotEqual(hash(ClosedNamespace('http://example.org/', 'foo')), hash(ClosedNamespace('http://example.org/', 'bar'))) + self.assertNotEqual(hash(ClosedNamespace('http://example.org/')), hash(Foo('http://example.org/'))) + self.assertNotEqual(hash(Foo('http://example.org/')), hash(ClosedNamespace('http://example.org/'))) + self.assertNotEqual(hash(ClosedNamespace('http://example.org/')), hash(ClosedNamespace('http://example.org#'))) + self.assertNotEqual(hash(ClosedNamespace('http://example.org#')), hash(ClosedNamespace('http://example.org/'))) + + def test_getattr(self): + self.assertEqual(ClosedNamespace('http://example.org/', 'foo', 'bar').foo, 'http://example.org/foo') + self.assertEqual(ClosedNamespace('http://example.org/', 'bar', 'bar').bar, 'http://example.org/bar') + self.assertEqual(ClosedNamespace('http://example.org/me#', 'foo', 'bar').foo, 'http://example.org/me#foo') + self.assertEqual(ClosedNamespace('http://example.org/me#', 'foo', 'bar').bar, 'http://example.org/me#bar') + self.assertRaises(KeyError, getattr, ClosedNamespace('http://example.org/', 'bar', 'bar'), 'foobar') + self.assertRaises(KeyError, getattr, ClosedNamespace('http://example.org#', 'bar', 'bar'), 'foobar') + + def test_getitem(self): + self.assertEqual(ClosedNamespace('http://example.org/', 'foo', 'bar')['foo'], 'http://example.org/foo') + self.assertEqual(ClosedNamespace('http://example.org/', 'foo', 'bar')['bar'], 'http://example.org/bar') + self.assertEqual(ClosedNamespace('http://example.org/me#', 'foo', 'bar')['foo'], 'http://example.org/me#foo') + self.assertEqual(ClosedNamespace('http://example.org/me#', 'foo', 'bar')['bar'], 'http://example.org/me#bar') + self.assertRaises(KeyError, ClosedNamespace('http://example.org/', 'bar', 'bar').__getitem__, 'foobar') + self.assertRaises(KeyError, ClosedNamespace('http://example.org#', 'bar', 'bar').__getitem__, 'foobar') + + +## main ## + +if __name__ == '__main__': + unittest.main() + +## EOF ## -- cgit v1.2.3 From 7eb61d117a995b076d36c55d2c7c268665360813 Mon Sep 17 00:00:00 2001 From: Matthias Baumgartner Date: Thu, 8 Dec 2022 16:34:13 +0100 Subject: schema --- test/schema/__init__.py | 0 test/schema/test_schema.py | 616 +++++++++++++++++++++++++++++++++++++++++++++ test/schema/test_types.py | 225 +++++++++++++++++ 3 files changed, 841 insertions(+) create mode 100644 test/schema/__init__.py create mode 100644 test/schema/test_schema.py create mode 100644 test/schema/test_types.py (limited to 'test') diff --git a/test/schema/__init__.py b/test/schema/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/schema/test_schema.py b/test/schema/test_schema.py new file mode 100644 index 0000000..2dc26e8 --- /dev/null +++ b/test/schema/test_schema.py @@ -0,0 +1,616 @@ +""" + +Part of the tagit test suite. +A copy of the license is provided with the project. +Author: Matthias Baumgartner, 2022 +""" +# imports +import operator +import unittest + +# bsfs imports +from bsfs.namespace import ns +from bsfs.schema import types +from bsfs.utils import errors + +# objects to test +from bsfs.schema.schema import Schema + + +## code ## + +class TestSchema(unittest.TestCase): + + def setUp(self): + self.schema_str = ''' + prefix rdfs: + prefix xsd: + + prefix bsfs: + prefix bse: + + bsfs:Entity rdfs:subClassOf bsfs:Node . + bsfs:Tag rdfs:subClassOf bsfs:Node . + bsfs:Image rdfs:subClassOf bsfs:Entity . + bsfs:Unused rdfs:subClassOf bsfs:Node . + + xsd:string rdfs:subClassOf bsfs:Literal . + xsd:integer rdfs:subClassOf bsfs:Literal . + xsd:boolean rdfs:subClassOf bsfs:Literal . + + bse:tag rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity ; + rdfs:range bsfs:Tag ; + bsfs:unique "false"^^xsd:boolean . + + bse:group rdfs:subClassOf bse:tag ; + rdfs:domain bsfs:Image ; + 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 "true"^^xsd:boolean . + + ''' + # nodes + self.n_root = types.Node(ns.bsfs.Node, None) + self.n_ent = types.Node(ns.bsfs.Entity, types.Node(ns.bsfs.Node, None)) + self.n_img = types.Node(ns.bsfs.Image, types.Node(ns.bsfs.Entity, types.Node(ns.bsfs.Node, None))) + self.n_tag = types.Node(ns.bsfs.Tag, types.Node(ns.bsfs.Node, None)) + self.n_unused = types.Node(ns.bsfs.Unused, types.Node(ns.bsfs.Node, None)) + self.nodes = [self.n_root, self.n_ent, self.n_img, self.n_tag, self.n_unused] + + # literals + self.l_root = types.Literal(ns.bsfs.Literal, None) + self.l_string = types.Literal(ns.xsd.string, types.Literal(ns.bsfs.Literal, None)) + self.l_integer = types.Literal(ns.xsd.integer, types.Literal(ns.bsfs.Literal, None)) + self.l_unused = types.Literal(ns.xsd.boolean, types.Literal(ns.bsfs.Literal, None)) + self.literals = [self.l_root, self.l_string, self.l_integer, self.l_unused] + + # predicates + self.p_root = types.Predicate(ns.bsfs.Predicate, None, types.Node(ns.bsfs.Node, None), None, False) + self.p_tag = self.p_root.get_child(ns.bse.tag, self.n_ent, self.n_tag, False) + self.p_group = self.p_tag.get_child(ns.bse.group, self.n_img, self.n_tag, False) + self.p_comment = self.p_root.get_child(ns.bse.comment, self.n_root, self.l_string, True) + self.predicates = [self.p_root, self.p_tag, self.p_group, self.p_comment] + + def test_construction(self): + # nodes and literals are optional + schema = Schema(self.predicates) + self.assertSetEqual(set(schema.nodes()), {self.n_root, self.n_ent, self.n_img, self.n_tag}) + self.assertSetEqual(set(schema.literals()), {self.l_root, self.l_string}) + self.assertSetEqual(set(schema.predicates()), set(self.predicates)) + + # predicates, nodes, and literals are respected + schema = Schema(self.predicates, self.nodes, self.literals) + self.assertSetEqual(set(schema.nodes()), set(self.nodes)) + self.assertSetEqual(set(schema.literals()), set(self.literals)) + self.assertSetEqual(set(schema.predicates()), set(self.predicates)) + + # nodes are complete (w/o unused) + schema = Schema(self.predicates, None, self.literals) + self.assertSetEqual(set(schema.nodes()), {self.n_root, self.n_ent, self.n_img, self.n_tag}) + schema = Schema(self.predicates, [], self.literals) + self.assertSetEqual(set(schema.nodes()), {self.n_root, self.n_ent, self.n_img, self.n_tag}) + schema = Schema(self.predicates, [self.n_img, self.n_tag], self.literals) + self.assertSetEqual(set(schema.nodes()), {self.n_root, self.n_ent, self.n_img, self.n_tag}) + schema = Schema(self.predicates, [self.n_unused], self.literals) + self.assertSetEqual(set(schema.nodes()), set(self.nodes)) + + # literals are complete + schema = Schema(self.predicates, self.nodes, None) + self.assertSetEqual(set(schema.literals()), {self.l_root, self.l_string}) + schema = Schema(self.predicates, self.nodes, []) + self.assertSetEqual(set(schema.literals()), {self.l_root, self.l_string}) + schema = Schema(self.predicates, self.nodes, [self.l_string]) + self.assertSetEqual(set(schema.literals()), {self.l_root, self.l_string}) + schema = Schema(self.predicates, self.nodes, [self.l_integer]) + self.assertSetEqual(set(schema.literals()), {self.l_root, self.l_string, self.l_integer}) + schema = Schema(self.predicates, self.nodes, [self.l_integer, self.l_unused]) + self.assertSetEqual(set(schema.literals()), set(self.literals)) + + # predicates are complete + schema = Schema([], self.nodes, self.literals) + self.assertSetEqual(set(schema.predicates()), set()) + schema = Schema([self.p_group], self.nodes, self.literals) + self.assertSetEqual(set(schema.predicates()), {self.p_root, self.p_tag, self.p_group}) + schema = Schema([self.p_group, self.p_comment], self.nodes, self.literals) + self.assertSetEqual(set(schema.predicates()), set(self.predicates)) + + # node uris must be unique + self.assertRaises(errors.ConsistencyError, Schema, self.predicates, + self.nodes + [types.Node(ns.bsfs.Entity, None)], self.literals) + self.assertRaises(errors.ConsistencyError, Schema, self.predicates, + self.nodes + [types.Node(ns.bsfs.Entity, types.Node(ns.bsfs.Foo, None))], self.literals) + self.assertRaises(errors.ConsistencyError, Schema, self.predicates, + self.nodes + [types.Node(ns.bsfs.Entity, self.n_img)], self.literals) + self.assertRaises(errors.ConsistencyError, Schema, self.predicates, + [types.Node(ns.bsfs.Entity, self.n_img)], self.literals) + + # literal uris must be unique + self.assertRaises(errors.ConsistencyError, Schema, self.predicates, self.nodes, + self.literals + [types.Literal(ns.xsd.string, None)]) + self.assertRaises(errors.ConsistencyError, Schema, self.predicates, self.nodes, + self.literals + [types.Literal(ns.xsd.string, types.Literal(ns.bsfs.Foo, None))]) + self.assertRaises(errors.ConsistencyError, Schema, self.predicates, self.nodes, + self.literals + [types.Literal(ns.xsd.string, self.l_integer)]) + self.assertRaises(errors.ConsistencyError, Schema, self.predicates, self.nodes, + [types.Literal(ns.xsd.string, self.l_integer)]) + + # predicate uris must be unique + self.assertRaises(errors.ConsistencyError, Schema, + self.predicates + [types.Predicate(ns.bse.tag, self.p_root, self.n_root, self.n_tag, False)]) + self.assertRaises(errors.ConsistencyError, Schema, + self.predicates + [types.Predicate(ns.bse.tag, self.p_root, self.n_ent, self.n_img, False)]) + self.assertRaises(errors.ConsistencyError, Schema, + self.predicates + [types.Predicate(ns.bse.tag, self.p_root, self.n_ent, self.n_tag, True)]) + self.assertRaises(errors.ConsistencyError, Schema, + self.predicates + [types.Predicate(ns.bse.tag, None, self.n_ent, self.n_tag, False)]) + + # uris must be unique across nodes, literals, and predicates + self.assertRaises(errors.ConsistencyError, Schema, + {}, {types.Node(ns.bsfs.Foo, None)}, {types.Node(ns.bsfs.Foo, None)}) + self.assertRaises(errors.ConsistencyError, Schema, + {types.Predicate(ns.bsfs.Foo, None, types.Node(ns.bsfs.Node, None), None, False)}, {}, {types.Node(ns.bsfs.Foo, None)}) + self.assertRaises(errors.ConsistencyError, Schema, + {types.Predicate(ns.bsfs.Foo, None, types.Node(ns.bsfs.Node, None), None, False)}, {types.Node(ns.bsfs.Foo, None)}, {}) + self.assertRaises(errors.ConsistencyError, Schema, + {types.Predicate(ns.bsfs.Foo, None, types.Node(ns.bsfs.Node, None), None, False)}, {types.Node(ns.bsfs.Foo, None)}, {types.Node(ns.bsfs.Foo, None)}) + + def test_str(self): + self.assertEqual(str(Schema([])), 'Schema()') + self.assertEqual(str(Schema([], [], [])), 'Schema()') + self.assertEqual(str(Schema(self.predicates, self.nodes, self.literals)), 'Schema()') + self.assertEqual(repr(Schema([])), 'Schema([], [], [])') + self.assertEqual(repr(Schema([], [], [])), 'Schema([], [], [])') + n = [ns.bsfs.Entity, ns.bsfs.Image, ns.bsfs.Node, ns.bsfs.Tag, ns.bsfs.Unused] + l = [ns.bsfs.Literal, ns.xsd.boolean, ns.xsd.integer, ns.xsd.string] + p = [ns.bse.comment, ns.bse.group, ns.bse.tag, ns.bsfs.Predicate] + self.assertEqual(repr(Schema(self.predicates, self.nodes, self.literals)), f'Schema({n}, {l}, {p})') + + def test_equality(self): + schema = Schema(self.predicates, self.nodes, self.literals) + # instance is equal to itself + self.assertEqual(schema, schema) + self.assertEqual(hash(schema), hash(schema)) + # instance is equal to a clone + self.assertEqual(schema, Schema(self.predicates, self.nodes, self.literals)) + self.assertEqual(hash(schema), hash(Schema(self.predicates, self.nodes, self.literals))) + # equality respects nodes + self.assertNotEqual(schema, + Schema(self.predicates, [self.n_root, self.n_ent, self.n_img, self.n_tag], self.literals)) + self.assertNotEqual(hash(schema), + hash(Schema(self.predicates, [self.n_root, self.n_ent, self.n_img, self.n_tag], self.literals))) + self.assertNotEqual(schema, + Schema(self.predicates, self.nodes + [types.Node(ns.bsfs.Document, self.n_ent)], self.literals)) + self.assertNotEqual(hash(schema), + hash(Schema(self.predicates, self.nodes + [types.Node(ns.bsfs.Document, self.n_ent)], self.literals))) + # equality respects literals + self.assertNotEqual(schema, + Schema(self.predicates, self.nodes, [self.l_root, self.l_string, self.l_integer])) + self.assertNotEqual(hash(schema), + hash(Schema(self.predicates, self.nodes, [self.l_root, self.l_string, self.l_integer]))) + self.assertNotEqual(schema, + Schema(self.predicates, self.nodes, self.literals + [types.Literal(ns.xsd.number, self.l_root)])) + self.assertNotEqual(hash(schema), + hash(Schema(self.predicates, self.nodes, self.literals + [types.Literal(ns.xsd.number, self.l_root)]))) + # equality respects predicates + self.assertNotEqual(schema, + Schema([self.p_group, self.p_tag, self.p_root], self.nodes, self.literals)) + self.assertNotEqual(hash(schema), + hash(Schema([self.p_group, self.p_tag, self.p_root], self.nodes, self.literals))) + self.assertNotEqual(schema, + Schema(self.predicates + [self.p_root.get_child(ns.bse.filesize, self.n_ent, self.l_integer)], self.nodes, self.literals)) + self.assertNotEqual(hash(schema), + hash(Schema(self.predicates + [self.p_root.get_child(ns.bse.filesize, self.n_ent, self.l_integer)], self.nodes, self.literals))) + + def test_diff(self): + # difference can be empty + diff = Schema({self.p_tag}).diff(Schema({self.p_group})) + self.assertSetEqual(set(diff.nodes), set()) + self.assertSetEqual(set(diff.literals), set()) + self.assertSetEqual(set(diff.predicates), set()) + + # difference contains predicates from the LHS + diff = Schema({self.p_group}).diff(Schema({self.p_tag})) + self.assertSetEqual(set(diff.nodes), {self.n_img}) + self.assertSetEqual(set(diff.literals), set()) + self.assertSetEqual(set(diff.predicates), {self.p_group}) + + # difference does not contain predicates from the RHS + diff = Schema({self.p_tag, self.p_comment}).diff(Schema({self.p_group})) + self.assertSetEqual(set(diff.nodes), set()) + self.assertSetEqual(set(diff.literals), {self.l_root, self.l_string}) + self.assertSetEqual(set(diff.predicates), {self.p_comment}) + + # difference considers extra nodes and literals + diff = Schema({self.p_tag}, {self.n_unused}, {self.l_unused}).diff(Schema({self.p_tag})) + self.assertSetEqual(set(diff.nodes), {self.n_unused}) + self.assertSetEqual(set(diff.literals), {self.l_root, self.l_unused}) + self.assertSetEqual(set(diff.predicates), set()) + + # difference considers inconsistent types + diff = Schema({self.p_tag}, {self.n_unused}, {self.l_unused}).diff( + Schema({self.p_tag}, {types.Node(ns.bsfs.Unused, None)}, {types.Literal(ns.xsd.boolean, None)})) + self.assertSetEqual(set(diff.nodes), {self.n_unused}) + self.assertSetEqual(set(diff.literals), {self.l_root, self.l_unused}) + self.assertSetEqual(set(diff.predicates), set()) + + # __sub__ is an alias for diff + diff = Schema({self.p_comment}, {self.n_unused}, {self.l_unused}) - Schema({self.p_group}) + self.assertSetEqual(set(diff.nodes), {self.n_unused}) + self.assertSetEqual(set(diff.literals), {self.l_root, self.l_string, self.l_unused}) + self.assertSetEqual(set(diff.predicates), {self.p_comment}) + # __sub__ only accepts Schema instances + class Foo(): pass + self.assertRaises(TypeError, operator.sub, Schema({self.p_comment}, {self.n_unused}, {self.l_unused}), 1234) + self.assertRaises(TypeError, operator.sub, Schema({self.p_comment}, {self.n_unused}, {self.l_unused}), 'hello world') + self.assertRaises(TypeError, operator.sub, Schema({self.p_comment}, {self.n_unused}, {self.l_unused}), Foo()) + + def test_consistent_with(self): + # argument must be a schema + class Foo(): pass + self.assertRaises(TypeError, Schema([]).consistent_with, 1234) + self.assertRaises(TypeError, Schema([]).consistent_with, 'hello world') + self.assertRaises(TypeError, Schema([]).consistent_with, Foo()) + + # node consistency + self.assertTrue(Schema([], {self.n_ent, self.n_tag, self.n_unused}).consistent_with( + Schema(self.predicates))) + self.assertFalse(Schema([], {types.Node(ns.bsfs.Entity, None)}).consistent_with( + Schema(self.predicates))) + # order doesn't matter + self.assertTrue(Schema(self.predicates).consistent_with( + Schema([], {self.n_ent, self.n_tag, self.n_unused}))) + + # literal consistency + self.assertTrue(Schema([], [], {self.l_string, self.l_unused}).consistent_with( + Schema(self.predicates))) + self.assertFalse(Schema([], [], {types.Literal(ns.xsd.string, None)}).consistent_with( + Schema(self.predicates))) + # order doesn't matter + self.assertTrue(Schema(self.predicates).consistent_with( + Schema([], [], {self.l_string, self.l_unused}))) + + # predicate consistency + self.assertTrue(Schema({self.p_tag}).consistent_with( + Schema(self.predicates))) + self.assertFalse(Schema({types.Predicate(ns.bse.tag, None, self.n_root, self.n_root, False)}).consistent_with( + Schema(self.predicates))) + # order doesn't matter + self.assertTrue(Schema(self.predicates).consistent_with( + Schema({self.p_tag}))) + + # global consistency + self.assertFalse(Schema({types.Predicate(ns.bsfs.Entity, None, self.n_root, self.n_root, False)}).consistent_with( + Schema(self.predicates))) + self.assertFalse(Schema([], {types.Node(ns.xsd.string, None)}).consistent_with( + Schema(self.predicates))) + self.assertFalse(Schema([], [], {types.Literal(ns.bsfs.Entity, None)}).consistent_with( + Schema(self.predicates))) + + + def test_union(self): + # must provide at least one schema + self.assertRaises(TypeError, Schema.Union) + + # can pass schemas as list + self.assertEqual(Schema.Union([Schema({self.p_tag})]), Schema({self.p_tag})) + self.assertEqual(Schema.Union([Schema({self.p_tag}), Schema({self.p_comment})]), + Schema({self.p_tag, self.p_comment})) + + # can pass schemas as arguments + self.assertEqual(Schema.Union(Schema({self.p_tag})), Schema({self.p_tag})) + self.assertEqual(Schema.Union(Schema({self.p_tag}), Schema({self.p_comment})), + Schema({self.p_tag, self.p_comment})) + + # cannot mix the two argument passing styles + self.assertRaises(TypeError, Schema.Union, [Schema(self.predicates)], Schema(self.predicates)) + + # all arguments must be Schema instances + self.assertRaises(TypeError, Schema.Union, Schema(self.predicates), 1234) + self.assertRaises(TypeError, Schema.Union, Schema(self.predicates), 1234, Schema(self.predicates)) + self.assertRaises(TypeError, Schema.Union, Schema(self.predicates), 'hello world') + + # Union merges predicates, nodes, and literals + self.assertEqual(Schema.Union( + Schema({self.p_comment}, {self.n_unused}, {}), + Schema({self.p_group}, {self.n_img}, {self.l_unused})), + Schema({self.p_comment, self.p_group}, {self.n_img, self.n_unused}, {self.l_unused})) + + # Union does not accept inconsistent nodes + self.assertRaises(errors.ConsistencyError, Schema.Union, Schema(self.predicates), + Schema({}, {types.Node(ns.bsfs.Entity, None)})) + self.assertRaises(errors.ConsistencyError, Schema.Union, Schema({}, {self.n_ent}), + Schema({}, {types.Node(ns.bsfs.Entity, None)})) + self.assertRaises(errors.ConsistencyError, Schema.Union, Schema({}, {self.n_ent}), + Schema({}, {}, {types.Literal(ns.bsfs.Entity, None)})) + + # Union does not accept inconsistent literals + self.assertRaises(errors.ConsistencyError, Schema.Union, Schema(self.predicates), + Schema({}, {}, {types.Literal(ns.xsd.string, None)})) + self.assertRaises(errors.ConsistencyError, Schema.Union, Schema({}, {}, {self.l_string}), + Schema({}, {}, {types.Literal(ns.xsd.string, None)})) + self.assertRaises(errors.ConsistencyError, Schema.Union, Schema({}, {}, {self.l_string}), + Schema({}, {types.Node(ns.xsd.string, None)})) + + # Union does not accept inconsistent predicates + self.assertRaises(errors.ConsistencyError, Schema.Union, Schema({self.p_tag}), + Schema({types.Predicate(ns.bse.tag, None, self.n_ent, self.n_tag, False)})) + self.assertRaises(errors.ConsistencyError, Schema.Union, Schema({self.p_tag}), + Schema({}, {types.Node(ns.bse.tag, None)})) + + # union is an alias for Union + self.assertEqual(Schema({self.p_comment}, {self.n_unused}, {}).union( + Schema({self.p_group}, {self.n_img}, {self.l_unused})), + Schema({self.p_comment, self.p_group}, {self.n_img, self.n_unused}, {self.l_unused})) + # union only accepts Schema instances + class Foo(): pass + self.assertRaises(TypeError, Schema({self.p_comment}, {self.n_unused}, {}).union, 1234) + self.assertRaises(TypeError, Schema({self.p_comment}, {self.n_unused}, {}).union, 'hello world') + self.assertRaises(TypeError, Schema({self.p_comment}, {self.n_unused}, {}).union, Foo()) + + # __add__ is an alias for Union + self.assertEqual(Schema({self.p_comment}, {self.n_unused}, {}) + Schema({self.p_group}, {self.n_img}, {self.l_unused}), + Schema({self.p_comment, self.p_group}, {self.n_img, self.n_unused}, {self.l_unused})) + # __add__ only accepts Schema instances + class Foo(): pass + self.assertRaises(TypeError, operator.add, Schema({self.p_comment}, {self.n_unused}, {}), 1234) + self.assertRaises(TypeError, operator.add, Schema({self.p_comment}, {self.n_unused}, {}), 'hello world') + self.assertRaises(TypeError, operator.add, Schema({self.p_comment}, {self.n_unused}, {}), Foo()) + + # __or__ is an alias for Union + self.assertEqual(Schema({self.p_comment}, {self.n_unused}, {}) | Schema({self.p_group}, {self.n_img}, {self.l_unused}), + Schema({self.p_comment, self.p_group}, {self.n_img, self.n_unused}, {self.l_unused})) + # __or__ only accepts Schema instances + class Foo(): pass + self.assertRaises(TypeError, operator.or_, Schema({self.p_comment}, {self.n_unused}, {}), 1234) + self.assertRaises(TypeError, operator.or_, Schema({self.p_comment}, {self.n_unused}, {}), 'hello world') + self.assertRaises(TypeError, operator.or_, Schema({self.p_comment}, {self.n_unused}, {}), Foo()) + + def test_type_getters(self): + schema = Schema(self.predicates, self.nodes, self.literals) + # nodes + self.assertEqual(self.n_root, schema.node(ns.bsfs.Node)) + self.assertEqual(self.n_ent, schema.node(ns.bsfs.Entity)) + self.assertEqual(self.n_img, schema.node(ns.bsfs.Image)) + self.assertRaises(KeyError, schema.node, ns.bsfs.Document) + self.assertRaises(KeyError, schema.node, self.n_root) + # literals + self.assertEqual(self.l_root, schema.literal(ns.bsfs.Literal)) + self.assertEqual(self.l_string, schema.literal(ns.xsd.string)) + self.assertEqual(self.l_integer, schema.literal(ns.xsd.integer)) + self.assertRaises(KeyError, schema.literal, ns.xsd.number) + self.assertRaises(KeyError, schema.literal, self.l_root) + # predicates + self.assertEqual(self.p_root, schema.predicate(ns.bsfs.Predicate)) + self.assertEqual(self.p_tag, schema.predicate(ns.bse.tag)) + self.assertEqual(self.p_group, schema.predicate(ns.bse.group)) + self.assertRaises(KeyError, schema.predicate, ns.bse.mimetype) + self.assertRaises(KeyError, schema.predicate, self.p_root) + + def test_list_getters(self): + schema = Schema(self.predicates, self.nodes, self.literals) + self.assertSetEqual(set(self.nodes), set(schema.nodes())) + self.assertSetEqual(set(self.literals), set(schema.literals())) + self.assertSetEqual(set(self.predicates), set(schema.predicates())) + + def test_has(self): + schema = Schema(self.predicates, self.nodes, self.literals) + # nodes + self.assertTrue(schema.has_node(ns.bsfs.Node)) + self.assertTrue(schema.has_node(ns.bsfs.Entity)) + self.assertTrue(schema.has_node(ns.bsfs.Image)) + self.assertFalse(schema.has_node(ns.bsfs.Document)) + self.assertFalse(schema.has_node(self.n_root)) + # literals + self.assertTrue(schema.has_literal(ns.bsfs.Literal)) + self.assertTrue(schema.has_literal(ns.xsd.string)) + self.assertTrue(schema.has_literal(ns.xsd.integer)) + self.assertFalse(schema.has_literal(ns.xsd.number)) + self.assertFalse(schema.has_literal(self.l_root)) + # predicates + self.assertTrue(schema.has_predicate(ns.bsfs.Predicate)) + self.assertTrue(schema.has_predicate(ns.bse.tag)) + self.assertTrue(schema.has_predicate(ns.bse.group)) + self.assertFalse(schema.has_predicate(ns.bse.mimetype)) + self.assertFalse(schema.has_predicate(self.p_root)) + + def test_empty(self): + self.assertEqual(Schema.Empty(), Schema( + [types.Predicate(ns.bsfs.Predicate, None, types.Node(ns.bsfs.Node, None), None, False)], + [types.Node(ns.bsfs.Node, None)], + [types.Literal(ns.bsfs.Literal, None)], + )) + + def test_from_string(self): + # from_string creates a schema + self.assertEqual( + Schema(self.predicates, self.nodes, self.literals), + Schema.from_string(self.schema_str)) + + # schema contains at least the root types + self.assertEqual(Schema.from_string(''), Schema({self.p_root}, {self.n_root}, {self.l_root})) + + # custom example + self.assertEqual( + Schema({types.Predicate(ns.bsfs.Predicate, None, self.n_root, None, False).get_child( + ns.bse.filename, self.n_ent, self.l_string, False)}), + Schema.from_string(''' + prefix rdfs: + prefix xsd: + prefix bsfs: + prefix bse: + + bsfs:Entity rdfs:subClassOf bsfs:Node . + xsd:string rdfs:subClassOf bsfs:Literal . + + bse:filename rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity ; + rdfs:range xsd:string ; + bsfs:unique "false"^^xsd:boolean . + ''')) + + # all nodes must be defined + self.assertRaises(errors.ConsistencyError, Schema.from_string, ''' + prefix rdfs: + prefix xsd: + prefix bsfs: + prefix bse: + + xsd:string rdfs:subClassOf bsfs:Literal . + + bse:filename rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity ; + rdfs:range xsd:string ; + bsfs:unique "false"^^xsd:boolean . + ''') + + # all literals must be defined + self.assertRaises(errors.ConsistencyError, Schema.from_string, ''' + prefix rdfs: + prefix xsd: + prefix bsfs: + prefix bse: + + bsfs:Entity rdfs:subClassOf bsfs:Node . + + bse:filename rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity ; + rdfs:range xsd:string ; + bsfs:unique "false"^^xsd:boolean . + ''') + + # must not have circular dependencies + self.assertRaises(errors.ConsistencyError, Schema.from_string, ''' + prefix rdfs: + prefix bsfs: + bsfs:Entity rdfs:subClassOf bsfs:Node . + # ah, a nice circular dependency + bsfs:Entity rdfs:subClassOf bsfs:Document . + bsfs:Document rdfs:subClassOf bsfs:Entity . + bsfs:PDF rdfs:subClassOf bsfs:Document . + ''') + + # range must be a node or literal + self.assertRaises(errors.ConsistencyError, Schema.from_string, ''' + prefix rdfs: + prefix xsd: + prefix bsfs: + prefix bse: + + bsfs:Entity rdfs:subClassOf bsfs:Node . + + bse:filename rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity ; + rdfs:range xsd:string ; + bsfs:unique "false"^^xsd:boolean . + ''') + self.assertRaises(errors.ConsistencyError, Schema.from_string, ''' + prefix rdfs: + prefix xsd: + prefix bsfs: + prefix bse: + + bsfs:Entity rdfs:subClassOf bsfs:Node . + + bse:filename rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity ; + rdfs:range bsfs:Foo ; + bsfs:unique "false"^^xsd:boolean . + ''') + self.assertRaises(errors.ConsistencyError, Schema.from_string, ''' + prefix rdfs: + prefix xsd: + prefix bsfs: + prefix bse: + + bsfs:Entity rdfs:subClassOf bsfs:Node . + + bse:filename rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity ; + rdfs:range bsfs:Predicate ; + bsfs:unique "false"^^xsd:boolean . + ''') + + # must be consistent + self.assertRaises(errors.ConsistencyError, Schema.from_string, ''' + prefix rdfs: + prefix xsd: + prefix bsfs: + + bsfs:Entity rdfs:subClassOf bsfs:Node . + bsfs:Document rdfs:subClassOf bsfs:Node . + bsfs:Document rdfs:subClassOf bsfs:Entity. + ''') + self.assertRaises(errors.ConsistencyError, Schema.from_string, ''' + prefix rdfs: + prefix xsd: + prefix bsfs: + + xsd:string rdfs:subClassOf bsfs:Literal . + xsd:name rdfs:subClassOf bsfs:Literal . + xsd:name rdfs:subClassOf xsd:string . + ''') + self.assertRaises(errors.ConsistencyError, Schema.from_string, ''' + prefix rdfs: + prefix xsd: + prefix bsfs: + prefix bse: + + bsfs:Entity rdfs:subClassOf bsfs:Node . + + bse:foo rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Node ; + rdfs:range bsfs:Node ; + bsfs:unique "false"^^xsd:boolean . + + bse:foo rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity . + + ''') + self.assertRaises(errors.ConsistencyError, Schema.from_string, ''' + prefix rdfs: + prefix xsd: + prefix bsfs: + prefix bse: + + bsfs:Entity rdfs:subClassOf bsfs:Node . + + bse:foo rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Node ; + rdfs:range bsfs:Node ; + bsfs:unique "false"^^xsd:boolean . + + bse:foo rdfs:subClassOf bsfs:Predicate ; + rdfs:range bsfs:Entity . + + ''') + self.assertRaises(errors.ConsistencyError, Schema.from_string, ''' + prefix rdfs: + prefix xsd: + prefix bsfs: + prefix bse: + + bsfs:Entity rdfs:subClassOf bsfs:Node . + + bse:foo rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Node ; + rdfs:range bsfs:Node ; + bsfs:unique "false"^^xsd:boolean . + + bse:foo rdfs:subClassOf bsfs:Predicate ; + bsfs:unique "true"^^xsd:boolean . + + ''') + + + +## main ## + +if __name__ == '__main__': + unittest.main() + +## EOF ## diff --git a/test/schema/test_types.py b/test/schema/test_types.py new file mode 100644 index 0000000..4a49e6e --- /dev/null +++ b/test/schema/test_types.py @@ -0,0 +1,225 @@ +""" + +Part of the tagit test suite. +A copy of the license is provided with the project. +Author: Matthias Baumgartner, 2022 +""" +# imports +import operator +import unittest + +# bsfs imports +from bsfs.namespace import ns +from bsfs.utils import errors + +# objects to test +from bsfs.schema.types import _Type, _Vertex, Node, Literal, Predicate + + +## code ## + +class TestType(unittest.TestCase): + def test_parents(self): + # create some types + fst = _Type('First') + snd = _Type('Second', fst) + trd = _Type('Third', snd) + frd = _Type('Fourth', trd) + # check parents + self.assertListEqual(list(fst.parents()), []) + self.assertListEqual(list(snd.parents()), [fst]) + self.assertListEqual(list(trd.parents()), [snd, fst]) + self.assertListEqual(list(frd.parents()), [trd, snd, fst]) + + def test_essentials(self): + # type w/o parent + self.assertEqual(str(_Type('Foo')), '_Type(Foo)') + self.assertEqual(repr(_Type('Foo')), '_Type(Foo, None)') + # type w/ parent + self.assertEqual(str(_Type('Foo', _Type('Bar'))), '_Type(Foo)') + self.assertEqual(repr(_Type('Foo', _Type('Bar'))), '_Type(Foo, _Type(Bar, None))') + # subtype w/o parent + class SubType(_Type): pass + self.assertEqual(str(SubType('Foo')), 'SubType(Foo)') + self.assertEqual(repr(SubType('Foo')), 'SubType(Foo, None)') + # subtype w/ parent + self.assertEqual(str(SubType('Foo', SubType('Bar'))), 'SubType(Foo)') + self.assertEqual(repr(SubType('Foo', SubType('Bar'))), 'SubType(Foo, SubType(Bar, None))') + # subtype and type mixed + self.assertEqual(str(SubType('Foo', _Type('Bar'))), 'SubType(Foo)') + self.assertEqual(repr(SubType('Foo', _Type('Bar'))), 'SubType(Foo, _Type(Bar, None))') + self.assertEqual(str(_Type('Foo', SubType('Bar'))), '_Type(Foo)') + self.assertEqual(repr(_Type('Foo', SubType('Bar'))), '_Type(Foo, SubType(Bar, None))') + + def test_get_child(self): + # callee is used as parent + self.assertEqual(_Type('First').get_child('Second'), _Type('Second', _Type('First'))) + # works with multiple parents + self.assertEqual(_Type('First').get_child('Second').get_child('Third'), _Type('Third', _Type('Second', _Type('First')))) + # type persists + class Foo(_Type): pass + self.assertEqual(Foo('First').get_child('Second'), Foo('Second', Foo('First'))) + + def test_equality(self): + # equality depends on uri + self.assertEqual(_Type('Foo'), _Type('Foo')) + self.assertEqual(hash(_Type('Foo')), hash(_Type('Foo'))) + self.assertNotEqual(_Type('Foo'), _Type('Bar')) + self.assertNotEqual(hash(_Type('Foo')), hash(_Type('Bar'))) + # comparison is case-sensitive + self.assertNotEqual(_Type('FOO'), _Type('foo')) + self.assertNotEqual(hash(_Type('FOO')), hash(_Type('foo'))) + # comparison respects type + class Foo(_Type): pass + self.assertNotEqual(_Type('Foo'), Foo('Foo')) + self.assertNotEqual(hash(_Type('Foo')), hash(Foo('Foo'))) + # comparison respects parent + self.assertNotEqual(_Type('Foo', _Type('Bar')), _Type('Foo')) + self.assertNotEqual(hash(_Type('Foo', _Type('Bar'))), hash(_Type('Foo'))) + + def test_order(self): + # create some types. + vehicle = _Type('Vehicle') + twowheel = _Type('Two-wheel', vehicle) + bike = _Type('Bike', twowheel) + bicycle = _Type('Bicycle', twowheel) + # two-wheel is equivalent to itself + self.assertFalse(twowheel == vehicle) + self.assertTrue(twowheel == twowheel) + self.assertFalse(twowheel == bicycle) + # two-wheel is a true subclass of Vehicle + self.assertTrue(twowheel < vehicle) + self.assertFalse(twowheel < twowheel) + self.assertFalse(twowheel < bicycle) + # two-wheel is a subclass of itself and Vehicle + self.assertTrue(twowheel <= vehicle) + self.assertTrue(twowheel <= twowheel) + self.assertFalse(twowheel <= bicycle) + # two-wheel is a true superclass of Bicycle + self.assertFalse(twowheel > vehicle) + self.assertFalse(twowheel > twowheel) + self.assertTrue(twowheel > bicycle) + # two-wheel is a superclass of itself and Bicycle + self.assertFalse(twowheel >= vehicle) + self.assertTrue(twowheel >= twowheel) + self.assertTrue(twowheel >= bicycle) + # analoguous to sets, this is not a total order + self.assertFalse(bike <= bicycle) + self.assertFalse(bike < bicycle) + self.assertFalse(bike > bicycle) + self.assertFalse(bike >= bicycle) + self.assertFalse(bike == bicycle) + class Foo(_Type): pass + foo = Foo(bike.uri, bike.parent) + # cannot compare different types + self.assertRaises(TypeError, operator.lt, foo, bike) + self.assertRaises(TypeError, operator.le, foo, bike) + self.assertRaises(TypeError, operator.gt, foo, bike) + self.assertRaises(TypeError, operator.ge, foo, bike) + # goes both ways + self.assertRaises(TypeError, operator.lt, bike, foo) + self.assertRaises(TypeError, operator.le, bike, foo) + self.assertRaises(TypeError, operator.gt, bike, foo) + self.assertRaises(TypeError, operator.ge, bike, foo) + +class TestPredicate(unittest.TestCase): + def test_construction(self): + # domain must be a node + self.assertRaises(TypeError, Predicate, ns.bse.foo, 1234, None, True) + self.assertRaises(TypeError, Predicate, ns.bse.foo, None, Literal(ns.bsfs.Foo, None), None, True) + # range must be None, a Literal, or a Node + self.assertRaises(TypeError, Predicate, ns.bse.foo, None, Node(ns.bsfs.Node, None), 1234, True) + self.assertRaises(TypeError, Predicate, ns.bse.foo, None, Node(ns.bsfs.Node, None), _Vertex(ns.bsfs.Foo, None), True) + self.assertRaises(TypeError, Predicate, ns.bse.foo, None, Node(ns.bsfs.Node, None), _Type(ns.bsfs.Foo, None), True) + class Foo(): pass + self.assertRaises(TypeError, Predicate, ns.bse.foo, None, Node(ns.bsfs.Node, None), Foo(), True) + + def test_equality(self): + n_root = Node(ns.bsfs.Node, None) + n_ent = Node(ns.bsfs.Entity, Node(ns.bsfs.Node, None)) + n_tag = Node(ns.bsfs.Tag, Node(ns.bsfs.Tag, None)) + root = Predicate( + uri=ns.bsfs.Predicate, + parent=None, + domain=n_root, + range=None, + unique=False, + ) + # instance is equal to itself + self.assertEqual(root, root) + self.assertEqual(hash(root), hash(root)) + # instance is equal to a clone + self.assertEqual(root, Predicate(ns.bsfs.Predicate, None, n_root, None, False)) + self.assertEqual(hash(root), hash(Predicate(ns.bsfs.Predicate, None, n_root, None, False))) + # equality respects uri + self.assertNotEqual(root, Predicate(ns.bsfs.Alternative, None, n_root, None, False)) + self.assertNotEqual(hash(root), hash(Predicate(ns.bsfs.Alternative, None, n_root, None, False))) + # equality respects parent + self.assertNotEqual(root, Predicate(ns.bsfs.Predicate, n_root, n_root, None, False)) + self.assertNotEqual(hash(root), hash(Predicate(ns.bsfs.Predicate, n_root, n_root, None, False))) + # equality respects domain + self.assertNotEqual(root, Predicate(ns.bsfs.Predicate, None, n_ent, None, False)) + self.assertNotEqual(hash(root), hash(Predicate(ns.bsfs.Predicate, None, n_ent, None, False))) + # equality respects range + self.assertNotEqual(root, Predicate(ns.bsfs.Predicate, None, n_root, n_root, False)) + self.assertNotEqual(hash(root), hash(Predicate(ns.bsfs.Predicate, None, n_root, n_root, False))) + # equality respects unique + self.assertNotEqual(root, Predicate(ns.bsfs.Predicate, None, n_root, None, True)) + self.assertNotEqual(hash(root), hash(Predicate(ns.bsfs.Predicate, None, n_root, None, True))) + + def test_get_child(self): + n_root = Node(ns.bsfs.Node, None) + n_ent = Node(ns.bsfs.Entity, Node(ns.bsfs.Node, None)) + n_tag = Node(ns.bsfs.Tag, Node(ns.bsfs.Tag, None)) + root = Predicate( + uri=ns.bsfs.Predicate, + parent=None, + domain=n_root, + range=None, + unique=False, + ) + tag = Predicate( + uri=ns.bsfs.Entity, + parent=root, + domain=n_ent, + range=n_tag, + unique=False, + ) + + # uri is respected + self.assertEqual(ns.bse.foo, tag.get_child(ns.bse.foo).uri) + # domain is respected + dom = Node(ns.bsfs.Image, n_ent) + self.assertEqual(dom, tag.get_child(ns.bse.foo, domain=dom).domain) + # range is respected + rng = Node(ns.bsfs.Group, n_tag) + self.assertEqual(rng, tag.get_child(ns.bse.foo, range=rng).range) + # cannot set range to None + self.assertEqual(n_tag, tag.get_child(ns.bse.foo, range=None).range) + # unique is respected + self.assertTrue(tag.get_child(ns.bse.foo, unique=True).unique) + + # domain is inherited from parent + self.assertEqual(n_ent, tag.get_child(ns.bse.foo).domain) + # range is inherited from parent + self.assertEqual(n_tag, tag.get_child(ns.bse.foo).range) + # uniqueness is inherited from parent + self.assertFalse(tag.get_child(ns.bse.foo).unique) + + # domain must be subtype of parent's domain + self.assertRaises(errors.ConsistencyError, tag.get_child, ns.bse.foo, domain=n_root) + self.assertRaises(errors.ConsistencyError, tag.get_child, ns.bse.foo, domain=Node(ns.bsfs.Image, n_root)) + # range cannot be None + self.assertRaises(ValueError, root.get_child, ns.bse.foo) + # range must be subtype of parent's range + self.assertRaises(errors.ConsistencyError, tag.get_child, ns.bse.foo, range=n_root) + self.assertRaises(errors.ConsistencyError, tag.get_child, ns.bse.foo, range=Node(ns.bsfs.Image, n_root)) + + +## main ## + +if __name__ == '__main__': + unittest.main() + +## EOF ## + -- cgit v1.2.3 From 547aa08b1f05ec0cdf725c34a7b1d1512b694063 Mon Sep 17 00:00:00 2001 From: Matthias Baumgartner Date: Thu, 8 Dec 2022 16:35:20 +0100 Subject: remaining essentials: uuid, errors --- test/utils/test_uuid.py | 92 +++++++++++++++++++++++++++++++++++++++++++++++++ test/utils/testfile.t | 1 + 2 files changed, 93 insertions(+) create mode 100644 test/utils/test_uuid.py create mode 100644 test/utils/testfile.t (limited to 'test') diff --git a/test/utils/test_uuid.py b/test/utils/test_uuid.py new file mode 100644 index 0000000..49176d4 --- /dev/null +++ b/test/utils/test_uuid.py @@ -0,0 +1,92 @@ +""" + +Part of the tagit test suite. +A copy of the license is provided with the project. +Author: Matthias Baumgartner, 2022 +""" +# imports +import os +import re +import unittest + +# objects to test +from bsfs.utils.uuid import UUID, UCID + + +## code ## + +class TestUUID(unittest.TestCase): + """Test the UUID generator. + + The UUID is expected to generate random strings of 64 characters(0-9, A-F, case insensitive). + Due to the random nature of UUIDs, we cannot actually check if an uid is 'valid' besides + matching the expected format. + + At best, we can check if the number of collisions (values generated repeatedly) is below some + threshold. One would expect the number of collisions to increase with the number of generated uids. + Hence, we only perform an empirical test, whereas the exact test parameters (NUM_SAMPLES, + COLLISIONS_THRESHOLD) are subject to the application requirements. Note that this simple test + cannot replace a thorough statistical analysis. + + """ + + # expected uuid string format + _RX_FORMAT = re.compile('[0-9A-Fa-f]{64}') + + # number of uuids to generate for collisions test + _NUM_SAMPLES = 100_000 + + # number of permitted collisions (less-than test; exclusive) + _COLLISIONS_THRESHOLD = 2 # zero or one collisions to pass the test + + def _test_format(self, uid): + self.assertIsInstance(uid, str) + self.assertTrue(self._RX_FORMAT.fullmatch(uid) is not None) + + def test_call(self): + gen = UUID() + # w/o content + self._test_format(gen()) + # with content + self._test_format(gen('hello world')) + + def test_iter(self): + for _, uid in zip(range(1_000), iter(UUID())): + self._test_format(uid) + + def test_next(self): + gen = UUID() + for _ in range(1_000): + uid = next(gen) + self._test_format(uid) + + def test_collisions(self): + # generated uuids are reasonably unique. + # Note that we cannot guarantee no collisions. + uids = {uid for _, uid in zip(range(self._NUM_SAMPLES), UUID())} + self.assertGreater(len(uids), self._NUM_SAMPLES - self._COLLISIONS_THRESHOLD) + # uuids are reasonably unique across instances + uidA = {uid for _, uid in zip(range(self._NUM_SAMPLES), UUID())} + uidB = {uid for _, uid in zip(range(self._NUM_SAMPLES), UUID())} + self.assertLess(len(uidA & uidB), self._COLLISIONS_THRESHOLD) + # uuids are reasonably unique despite identical seeds. + uidA = {uid for _, uid in zip(range(self._NUM_SAMPLES), UUID(seed=123))} + uidB = {uid for _, uid in zip(range(self._NUM_SAMPLES), UUID(seed=123))} + self.assertLess(len(uidA & uidB), self._COLLISIONS_THRESHOLD) + + +class TestUCID(unittest.TestCase): + def setUp(self): + self._checksum = 'a948904f2f0f479b8f8197694b30184b0d2ed1c1cd2a1ec0fb85d299a192a447' # sha256 + self._path = os.path.join(os.path.dirname(__file__), 'testfile.t') + + def test_from_path(self): + self.assertEqual(UCID.from_path(self._path), self._checksum) + + +## main ## + +if __name__ == '__main__': + unittest.main() + +## EOF ## diff --git a/test/utils/testfile.t b/test/utils/testfile.t new file mode 100644 index 0000000..3b18e51 --- /dev/null +++ b/test/utils/testfile.t @@ -0,0 +1 @@ +hello world -- cgit v1.2.3 From e8492489098ef5f8566214e083cd2c2d1d449f5a Mon Sep 17 00:00:00 2001 From: Matthias Baumgartner Date: Thu, 8 Dec 2022 16:36:19 +0100 Subject: sparql triple store and graph (nodes, mostly) --- test/graph/__init__.py | 0 test/graph/ac/__init__.py | 0 test/graph/ac/test_null.py | 102 ++++++ test/graph/test_graph.py | 93 +++++ test/graph/test_nodes.py | 361 ++++++++++++++++++ test/triple_store/__init__.py | 0 test/triple_store/test_base.py | 150 ++++++++ test/triple_store/test_sparql.py | 769 +++++++++++++++++++++++++++++++++++++++ 8 files changed, 1475 insertions(+) create mode 100644 test/graph/__init__.py create mode 100644 test/graph/ac/__init__.py create mode 100644 test/graph/ac/test_null.py create mode 100644 test/graph/test_graph.py create mode 100644 test/graph/test_nodes.py create mode 100644 test/triple_store/__init__.py create mode 100644 test/triple_store/test_base.py create mode 100644 test/triple_store/test_sparql.py (limited to 'test') diff --git a/test/graph/__init__.py b/test/graph/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/graph/ac/__init__.py b/test/graph/ac/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/graph/ac/test_null.py b/test/graph/ac/test_null.py new file mode 100644 index 0000000..f39c9be --- /dev/null +++ b/test/graph/ac/test_null.py @@ -0,0 +1,102 @@ +""" + +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.namespace import ns +from bsfs.triple_store import SparqlStore +from bsfs.utils import URI + +# objects to test +from bsfs.graph.ac.null import NullAC + + +## code ## + +class TestNullAC(unittest.TestCase): + def setUp(self): + self.backend = SparqlStore() + self.backend.schema = _schema.Schema.from_string(''' + prefix rdfs: + prefix xsd: + + prefix bsfs: + prefix bsm: + prefix bse: + + bsfs:Entity rdfs:subClassOf bsfs:Node . + bsfs:Tag rdfs:subClassOf bsfs:Node . + xsd:string rdfs:subClassOf bsfs:Literal . + xsd:integer rdfs:subClassOf bsfs:Literal . + + # predicates mandated by Nodes + bsm:t_created rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Node ; + rdfs:range xsd:integer ; + bsfs:unique "true"^^xsd:boolean . + + # additionally defined predicates + bse:tag rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity ; + rdfs:range bsfs:Tag ; + bsfs:unique "false"^^xsd:boolean . + + bse:author rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity ; + rdfs:range xsd:string ; + bsfs:unique "true"^^xsd:boolean . + + bse:filesize rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity ; + rdfs:range xsd:integer ; + bsfs:unique "false"^^xsd:boolean . + + ''') + self.user = URI('http://www.example.com/me') + 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.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_is_protected_predicate(self): + ac = NullAC(self.backend, self.user) + self.assertTrue(ac.is_protected_predicate(self.p_created)) + self.assertFalse(ac.is_protected_predicate(self.p_filesize)) + self.assertFalse(ac.is_protected_predicate(self.p_author)) + self.assertFalse(ac.is_protected_predicate(self.p_tag)) + + def test_create(self): + ac = NullAC(self.backend, self.user) + self.assertEqual(None, ac.create(self.ent_type, self.ent_ids)) + + def test_link_from_node(self): + ac = NullAC(self.backend, self.user) + self.assertSetEqual(self.ent_ids, ac.link_from_node(self.ent_type, self.ent_ids)) + + def test_link_to_node(self): + ac = NullAC(self.backend, self.user) + self.assertSetEqual(self.ent_ids, ac.link_to_node(self.ent_type, self.ent_ids)) + + def test_write_literal(self): + ac = NullAC(self.backend, self.user) + self.assertSetEqual(self.ent_ids, ac.write_literal(self.ent_type, self.ent_ids)) + + def test_createable(self): + ac = NullAC(self.backend, self.user) + self.assertSetEqual(self.ent_ids, ac.createable(self.ent_type, self.ent_ids)) + + +## main ## + +if __name__ == '__main__': + unittest.main() + +## EOF ## diff --git a/test/graph/test_graph.py b/test/graph/test_graph.py new file mode 100644 index 0000000..96046ef --- /dev/null +++ b/test/graph/test_graph.py @@ -0,0 +1,93 @@ +""" + +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.namespace import ns +from bsfs.triple_store import SparqlStore +from bsfs.utils import URI +from bsfs.graph.nodes import Nodes + +# objects to test +from bsfs.graph.graph import Graph + + +## code ## + +class TestGraph(unittest.TestCase): + def setUp(self): + self.user = URI('http://example.com/me') + self.backend = SparqlStore.Open(None) + self.backend.schema = _schema.Schema.from_string(''' + prefix rdfs: + prefix bsfs: + bsfs:Entity rdfs:subClassOf bsfs:Node . + ''') + + 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)') + # str respects backend + class Foo(SparqlStore): pass + self.assertEqual(str(Graph(Foo.Open(None), self.user)), + 'Graph(Foo(uri=None), http://example.com/me)') + self.assertEqual(repr(Graph(Foo.Open(None), self.user)), + 'Graph(backend=Foo(uri=None), user=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)') + # 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)') + + def test_equality(self): + graph = Graph(self.backend, self.user) + # 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))) + # equality respects backend + self.assertNotEqual(graph, Graph(SparqlStore.Open(None), self.user)) + self.assertNotEqual(hash(graph), hash(Graph(SparqlStore.Open(None), self.user))) + # 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) + # schema + self.assertEqual(graph.schema, self.backend.schema) + self.assertRaises(AttributeError, setattr, graph, 'schema', None) + + def test_nodes(self): + graph = Graph(self.backend, self.user) + 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)) + # node_type must be in the schema + self.assertRaises(KeyError, graph.nodes, ns.bsfs.Invalid, guids) + + +## main ## + +if __name__ == '__main__': + unittest.main() + +## EOF ## diff --git a/test/graph/test_nodes.py b/test/graph/test_nodes.py new file mode 100644 index 0000000..1fbd1e5 --- /dev/null +++ b/test/graph/test_nodes.py @@ -0,0 +1,361 @@ +""" + +Part of the bsfs test suite. +A copy of the license is provided with the project. +Author: Matthias Baumgartner, 2022 +""" +# imports +import rdflib +import unittest + +# bsie imports +from bsfs import schema as _schema +from bsfs.namespace import ns +from bsfs.triple_store.sparql import SparqlStore +from bsfs.utils import errors, URI + +# objects to test +from bsfs.graph.nodes import Nodes + + +## code ## + +class TestNodes(unittest.TestCase): + def setUp(self): + # initialize backend + self.backend = SparqlStore() + self.backend.schema = _schema.Schema.from_string(''' + prefix rdfs: + prefix xsd: + + prefix bsfs: + prefix bsm: + prefix bse: + prefix bst: + + 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 . + + # predicates mandated by Nodes + bsm:t_created rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Node ; + rdfs:range xsd:integer ; + bsfs:unique "true"^^xsd:boolean . + + # additionally defined predicates + 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 "true"^^xsd:boolean . + + bse:tag rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity ; + rdfs:range bsfs:Tag ; + bsfs:unique "false"^^xsd:boolean . + + bse:author rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity ; + rdfs:range bsfs:User ; + bsfs:unique "true"^^xsd:boolean . + + bst:representative rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Tag ; + rdfs:range bsfs:Entity ; + bsfs:unique "true"^^xsd:boolean . + + ''') + # Nodes constructor args + self.user = URI('http://example.com/me') + # set args + self.tag_type = self.backend.schema.node(ns.bsfs.Tag) + self.ent_type = self.backend.schema.node(ns.bsfs.Entity) + self.user_type = self.backend.schema.node(ns.bsfs.User) + 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.ent_ids = { + URI('http://example.com/me/entity#1234'), + URI('http://example.com/me/entity#4321'), + } + self.tag_ids = { + URI('http://example.com/me/tag#1234'), + URI('http://example.com/me/tag#4321'), + } + + 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})') + # 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})') + # str respects guids + nodes = Nodes(self.backend, self.user, 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\'}})') + # repr respects backend + class Foo(SparqlStore): pass + backend = Foo.Open(None) + 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})') + # 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})') + + def test_equality(self): + nodes = Nodes(self.backend, self.user, 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))) + # equality respects backend + backend = SparqlStore.Open(None) + 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))) + # 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))) + # 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))) + # 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))) + + 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.assertEqual(self.tag_type, Nodes( + self.backend, self.user, 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.assertSetEqual(self.tag_ids, set(Nodes( + self.backend, self.user, self.tag_type, self.tag_ids).guids)) + + def test__ensure_nodes(self): + nodes = Nodes(self.backend, self.user, self.ent_type, self.ent_ids) + + # missing nodes are created + self.assertSetEqual(self.ent_ids, nodes._ensure_nodes(self.ent_type, self.ent_ids)) + # get creation time from backend manually + 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), { + # 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')), + # 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)), + }) + + # existing nodes remain unchanged + self.assertSetEqual(self.ent_ids, nodes._ensure_nodes(self.ent_type, self.ent_ids)) + self.assertSetEqual(set(self.backend.graph), { + # 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')), + # 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)), + }) + + # type and guids don't need to match the node instance's members + self.assertSetEqual(self.tag_ids, nodes._ensure_nodes(self.tag_type, self.tag_ids)) + # 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 + # check triples + self.assertSetEqual(set(self.backend.graph), { + # 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.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.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()) + set_ = nodes._Nodes__set + + # node_type must match predicate's domain + self.assertRaises(errors.ConsistencyError, set_, self.p_representative.uri, self.ent_ids) + + # cannot set protected predicates + self.assertRaises(errors.PermissionDeniedError, set_, self.t_created.uri, 1234) + + # set literal value + set_(self.p_filesize.uri, 1234) + # get creation time from backend manually + 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), { + # 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')), + # 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)), + # literals + (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)), + }) + + # 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')}) + 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), { + # 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.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')), + # 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)), + # entity -> tag links + (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#4321'), 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')), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(self.p_tag.uri), rdflib.URIRef('http://example.com/me/tag#4321')), + }) + # value must be a nodes instance + self.assertRaises(TypeError, set_, self.p_tag.uri, 'foobar') + self.assertRaises(TypeError, set_, self.p_tag.uri, self.tag_ids) + 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)) + + def test_set(self): + self.assertSetEqual(set(self.backend.graph), set()) + nodes = Nodes(self.backend, self.user, 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')), + # 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.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')), + # 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')), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(self.p_tag.uri), rdflib.URIRef('http://example.com/me/tag#1234')), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(self.p_tag.uri), rdflib.URIRef('http://example.com/me/tag#4321')), + })) + + # cannot set protected predicate + curr = set(self.backend.graph) + self.assertRaises(errors.PermissionDeniedError, nodes.set, self.t_created.uri, 12345) + self.assertSetEqual(curr, set(self.backend.graph)) + # predicate.domain must match node_type + self.assertRaises(errors.ConsistencyError, nodes.set, self.p_representative.uri, nodes) + self.assertSetEqual(curr, set(self.backend.graph)) + # value's node_type must match predicate's range + self.assertRaises(errors.ConsistencyError, nodes.set, self.p_tag.uri, nodes) + self.assertSetEqual(curr, set(self.backend.graph)) + # value type must match predicate's range type + self.assertRaises(TypeError, nodes.set, self.p_tag.uri, 'invalid') + 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')})) + self.assertSetEqual(curr, set(self.backend.graph)) + + + 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) + # 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), + }.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')), + # 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)), + (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')), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(self.p_tag.uri), rdflib.URIRef('http://example.com/me/tag#1234')), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(self.p_tag.uri), rdflib.URIRef('http://example.com/me/tag#4321')), + })) + + # cannot set protected predicate + curr = set(self.backend.graph) + self.assertRaises(errors.PermissionDeniedError, nodes.set_from_iterable, ((self.p_filesize.uri, 1234), (self.t_created.uri, 12345))) + self.assertSetEqual(curr, set(self.backend.graph)) + # predicate.domain must match node_type + self.assertRaises(errors.ConsistencyError, nodes.set_from_iterable, ((self.p_filesize.uri, 1234), (self.p_representative.uri, nodes))) + self.assertSetEqual(curr, set(self.backend.graph)) + # value's node_type must match predicate's range + self.assertRaises(errors.ConsistencyError, nodes.set_from_iterable, ((self.p_filesize.uri, 1234), (self.p_tag.uri, nodes))) + self.assertSetEqual(curr, set(self.backend.graph)) + # value type must match predicate's range type + self.assertRaises(TypeError, nodes.set_from_iterable, ((self.p_filesize.uri, 1234), (self.p_tag.uri, 'invalid'))) + 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.assertSetEqual(curr, set(self.backend.graph)) + + +## main ## + +if __name__ == '__main__': + unittest.main() + +## EOF ## diff --git a/test/triple_store/__init__.py b/test/triple_store/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/triple_store/test_base.py b/test/triple_store/test_base.py new file mode 100644 index 0000000..a4b0559 --- /dev/null +++ b/test/triple_store/test_base.py @@ -0,0 +1,150 @@ +""" + +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.utils import URI + +# objects to test +from bsfs.triple_store.base import TripleStoreBase + + +## code ## + +class DummyBase(TripleStoreBase): + @classmethod + def Open(cls, uri, **kwargs): + return cls(uri) + + def commit(self): + pass + + def rollback(self): + pass + + @property + def schema(self): + pass + + @schema.setter + def schema(self, schema): + pass + + def exists(self, node_type, guids): + pass + + def create(self, node_type, guids): + pass + + def set(self, node_type, guids, predicate, values): + pass + +class DummyStore(DummyBase): + pass + +class DummyAlternative(DummyBase): + pass + + +class TestTripleStoreBase(unittest.TestCase): + + def test_equality(self): + # identical instances are equal + store = DummyStore.Open(None) + self.assertEqual(store, store) + self.assertEqual(hash(store), hash(store)) + store = DummyStore.Open(URI('http://example.com/store')) + self.assertEqual(store, store) + self.assertEqual(hash(store), hash(store)) + # in-memory storages are not equal + # NOTE: Don't use + # >>> self.assertNotEqual(hash(DummyStore(None)), hash(DummyStore(None))) + # The two stores are created subsequently since each of them is deleted + # right after hashing. Because the two instances never exist at the same + # time, their id may (and typically will) be identical. + # This only matters when the `id` function is used, i.e. when uri=None. + a, b = DummyStore.Open(None), DummyStore.Open(None) + self.assertNotEqual(a, b) + self.assertNotEqual(hash(a), hash(b)) + a, b = DummyStore.Open(None), DummyStore.Open(URI('http://example.com/store')) + self.assertNotEqual(a, b) + self.assertNotEqual(hash(a), hash(b)) + a, b = DummyStore.Open(URI('http://example.com/store')), DummyStore.Open(None) + self.assertNotEqual(a, b) + self.assertNotEqual(hash(a), hash(b)) + a, b = DummyStore.Open(None), DummyStore.Open(URI('http://example.com/alternative')) + self.assertNotEqual(a, b) + self.assertNotEqual(hash(a), hash(b)) + # equality respects uri + self.assertEqual( + DummyStore.Open(URI('http://example.com/store')), + DummyStore.Open(URI('http://example.com/store'))) + self.assertEqual( + hash(DummyStore.Open(URI('http://example.com/alternative'))), + hash(DummyStore.Open(URI('http://example.com/alternative')))) + self.assertNotEqual( + DummyStore.Open(URI('http://example.com/store')), + DummyStore.Open(URI('http://example.com/alternative'))) + self.assertNotEqual( + DummyStore.Open(URI('http://example.com/store')), + hash(DummyStore.Open(URI('http://example.com/alternative')))) + # equality respects type + self.assertNotEqual(DummyStore.Open(None), None) + self.assertNotEqual(hash(DummyStore.Open(None)), hash(None)) + self.assertNotEqual(DummyStore.Open(None), 'hello world') + self.assertNotEqual(hash(DummyStore.Open(None)), hash('hello world')) + self.assertNotEqual(DummyStore.Open(None), 1234) + self.assertNotEqual(hash(DummyStore.Open(None)), hash(1234)) + class Foo(): pass + f = Foo() + self.assertNotEqual(DummyStore.Open(None), f) + self.assertNotEqual(hash(DummyStore.Open(None)), hash(f)) + self.assertNotEqual( + DummyStore.Open(None), + DummyAlternative.Open(None)) + self.assertNotEqual( + hash(DummyStore.Open(None)), + hash(DummyAlternative.Open(None))) + + def test_string_conversion(self): + # string conversion respects uri + self.assertEqual('DummyStore(uri=http://example.com/store)', + str(DummyStore.Open(URI('http://example.com/store')))) + self.assertEqual('DummyStore(uri=http://example.com/store)', + repr(DummyStore.Open(URI('http://example.com/store')))) + self.assertEqual('DummyStore(uri=http://example.com/alternative)', + str(DummyStore.Open(URI('http://example.com/alternative')))) + self.assertEqual('DummyStore(uri=http://example.com/alternative)', + repr(DummyStore.Open(URI('http://example.com/alternative')))) + self.assertEqual('DummyStore(uri=None)', + str(DummyStore.Open(None))) + self.assertEqual('DummyStore(uri=None)', + repr(DummyStore.Open(None))) + # string conversion respects type + self.assertEqual('DummyAlternative(uri=http://example.com/store)', + str(DummyAlternative.Open(URI('http://example.com/store')))) + + def test_uri(self): + # uri returns correct value + self.assertEqual(None, + DummyStore.Open(None).uri) + self.assertEqual(URI('http://example.com/store'), + DummyStore.Open(URI('http://example.com/store')).uri) + self.assertEqual(URI('http://example.com/alternative'), + DummyStore.Open(URI('http://example.com/alternative')).uri) + # persistence respects uri + self.assertFalse(DummyStore.Open(None).is_persistent()) + self.assertTrue(DummyStore.Open(URI('http://example.com/store')).is_persistent()) + self.assertTrue(DummyStore.Open(URI('http://example.com/alternative')).is_persistent()) + +## main ## + +if __name__ == '__main__': + unittest.main() + +## EOF ## diff --git a/test/triple_store/test_sparql.py b/test/triple_store/test_sparql.py new file mode 100644 index 0000000..f047235 --- /dev/null +++ b/test/triple_store/test_sparql.py @@ -0,0 +1,769 @@ +""" + +Part of the bsfs test suite. +A copy of the license is provided with the project. +Author: Matthias Baumgartner, 2022 +""" +# imports +import rdflib +import unittest + +# bsie imports +from bsfs import schema as _schema +from bsfs.namespace import ns +from bsfs.utils import errors, URI + +# objects to test +from bsfs.triple_store.sparql import SparqlStore + + +## code ## + +class TestSparqlStore(unittest.TestCase): + def setUp(self): + self.schema = _schema.Schema.from_string(''' + prefix rdfs: + prefix xsd: + + prefix bsfs: + prefix bse: + + 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 . + + # non-unique literal + bse:comment rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity ; + rdfs:range xsd:string ; + bsfs:unique "false"^^xsd:boolean . + + # unique literal + bse:filesize rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity ; + rdfs:range xsd:integer ; + bsfs:unique "true"^^xsd:boolean . + + # non-unique node + bse:tag rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity ; + rdfs:range bsfs:Tag ; + bsfs:unique "false"^^xsd:boolean . + + # unique node + bse:author rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity ; + rdfs:range bsfs:User ; + bsfs:unique "true"^^xsd:boolean . + + ''') + + def test_essentials(self): + store = SparqlStore(None) + # equality + self.assertEqual(store, store) + self.assertEqual(hash(store), hash(store)) + self.assertNotEqual(store, SparqlStore(None)) + self.assertNotEqual(hash(store), hash(SparqlStore(None))) + # string conversion + self.assertEqual(str(store), 'SparqlStore(uri=None)') + self.assertEqual(repr(store), 'SparqlStore(uri=None)') + # open + self.assertIsInstance(SparqlStore.Open(None), SparqlStore) + + + def test__has_type(self): + # setup store + store = SparqlStore(None) + store.schema = _schema.Schema.from_string(''' + prefix rdfs: + prefix xsd: + prefix bsfs: + + bsfs:Entity rdfs:subClassOf bsfs:Node . + bsfs:Document rdfs:subClassOf bsfs:Entity . + bsfs:Image rdfs:subClassOf bsfs:Entity . + bsfs:PDF rdfs:subClassOf bsfs:Document . + + ''') + # add some instances + store.create(store.schema.node(ns.bsfs.Entity), {URI('http://example.com/me/entity#1234')}) + store.create(store.schema.node(ns.bsfs.Document), {URI('http://example.com/me/document#1234')}) + store.create(store.schema.node(ns.bsfs.Image), {URI('http://example.com/me/image#1234')}) + store.create(store.schema.node(ns.bsfs.PDF), {URI('http://example.com/me/pdf#1234')}) + + # node_type must be in the schema + self.assertRaises(errors.ConsistencyError, store._has_type, URI('http://example.com/me/entity#1234'), store.schema.node(ns.bsfs.Node).get_child(ns.bsfs.invalid)) + + # returns False on inexistent nodes + self.assertFalse(store._has_type(URI('http://example.com/me/entity#4321'), store.schema.node(ns.bsfs.Entity))) + self.assertFalse(store._has_type(URI('http://example.com/me/document#4321'), store.schema.node(ns.bsfs.Document))) + self.assertFalse(store._has_type(URI('http://example.com/me/image#4321'), store.schema.node(ns.bsfs.Image))) + self.assertFalse(store._has_type(URI('http://example.com/me/pdf#4321'), store.schema.node(ns.bsfs.PDF))) + + # _has_type checks direct types + self.assertTrue(store._has_type(URI('http://example.com/me/entity#1234'), store.schema.node(ns.bsfs.Entity))) + self.assertTrue(store._has_type(URI('http://example.com/me/document#1234'), store.schema.node(ns.bsfs.Document))) + self.assertTrue(store._has_type(URI('http://example.com/me/image#1234'), store.schema.node(ns.bsfs.Image))) + self.assertTrue(store._has_type(URI('http://example.com/me/pdf#1234'), store.schema.node(ns.bsfs.PDF))) + + # _has_type checks type hierarchy + self.assertFalse(store._has_type(URI('http://example.com/me/entity#1234'), store.schema.node(ns.bsfs.Document))) + self.assertFalse(store._has_type(URI('http://example.com/me/entity#1234'), store.schema.node(ns.bsfs.Image))) + self.assertFalse(store._has_type(URI('http://example.com/me/entity#1234'), store.schema.node(ns.bsfs.PDF))) + + self.assertTrue(store._has_type(URI('http://example.com/me/document#1234'), store.schema.node(ns.bsfs.Entity))) + self.assertFalse(store._has_type(URI('http://example.com/me/document#1234'), store.schema.node(ns.bsfs.Image))) + self.assertFalse(store._has_type(URI('http://example.com/me/document#1234'), store.schema.node(ns.bsfs.PDF))) + + self.assertTrue(store._has_type(URI('http://example.com/me/image#1234'), store.schema.node(ns.bsfs.Entity))) + self.assertFalse(store._has_type(URI('http://example.com/me/image#1234'), store.schema.node(ns.bsfs.Document))) + self.assertFalse(store._has_type(URI('http://example.com/me/image#1234'), store.schema.node(ns.bsfs.PDF))) + + self.assertTrue(store._has_type(URI('http://example.com/me/pdf#1234'), store.schema.node(ns.bsfs.Entity))) + self.assertTrue(store._has_type(URI('http://example.com/me/pdf#1234'), store.schema.node(ns.bsfs.Document))) + self.assertFalse(store._has_type(URI('http://example.com/me/pdf#1234'), store.schema.node(ns.bsfs.Image))) + + + def test_schema(self): + # setup + store = SparqlStore(None) + curr = self.schema + p_comment = curr.predicate(ns.bse.comment) + p_filesize = curr.predicate(ns.bse.filesize) + p_tag = curr.predicate(ns.bse.tag) + p_author = curr.predicate(ns.bse.author) + + # migrate to an initial schema + store.schema = curr + # store has migrated + self.assertEqual(store.schema, curr) + + # add some instances + ent_ids = {URI('http://example.com/me/entity#1234'), URI('http://example.com/me/entity#4321')} + tag_ids = {URI('http://example.com/me/tag#1234'), URI('http://example.com/me/tag#4321')} + store.create(curr.node(ns.bsfs.Entity), ent_ids) + store.create(curr.node(ns.bsfs.Tag), tag_ids) + store.create(curr.node(ns.bsfs.User), {URI('http://example.com/me')}) + # add some triples + store.set(curr.node(ns.bsfs.Entity), ent_ids, p_comment, {'foo', 'bar'}) + store.set(curr.node(ns.bsfs.Entity), ent_ids, p_filesize, {1234}) + store.set(curr.node(ns.bsfs.Entity), ent_ids, p_tag, + {URI('http://example.com/me/tag#1234'), URI('http://example.com/me/tag#4321')}) + store.set(curr.node(ns.bsfs.Entity), ent_ids, p_author, + {URI('http://example.com/me')}) + # check instances + instances = { + # node instances + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Entity)), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Entity)), + (rdflib.URIRef('http://example.com/me/tag#1234'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Tag)), + (rdflib.URIRef('http://example.com/me/tag#4321'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Tag)), + (rdflib.URIRef('http://example.com/me'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.User)), + # comments + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_comment.uri), rdflib.Literal('foo', datatype=rdflib.XSD.string)), + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_comment.uri), rdflib.Literal('bar', datatype=rdflib.XSD.string)), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_comment.uri), rdflib.Literal('foo', datatype=rdflib.XSD.string)), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_comment.uri), rdflib.Literal('bar', datatype=rdflib.XSD.string)), + # filesize + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_filesize.uri), rdflib.Literal('1234', datatype=rdflib.XSD.integer)), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_filesize.uri), rdflib.Literal('1234', datatype=rdflib.XSD.integer)), + # tags + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_tag.uri), rdflib.URIRef('http://example.com/me/tag#1234')), + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_tag.uri), rdflib.URIRef('http://example.com/me/tag#4321')), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_tag.uri), rdflib.URIRef('http://example.com/me/tag#1234')), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_tag.uri), rdflib.URIRef('http://example.com/me/tag#4321')), + # author + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_author.uri), rdflib.URIRef('http://example.com/me')), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_author.uri), rdflib.URIRef('http://example.com/me')), + } + self.assertSetEqual(set(store.graph), instances) + + # add some classes to the schema + curr = curr + _schema.Schema.from_string(''' + prefix rdfs: + prefix xsd: + prefix bsfs: + prefix bse: + prefix bst: + prefix bsc: + + bsfs:Entity rdfs:subClassOf bsfs:Node . + bsfs:Tag rdfs:subClassOf bsfs:Node . + bsfs:Collection rdfs:subClassOf bsfs:Node . + xsd:boolean rdfs:subClassOf bsfs:Literal . + + # literal + bse:shared rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity ; + rdfs:range xsd:boolean ; + bsfs:unique "true"^^xsd:boolean . + + # node + bse:partOf rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity ; + rdfs:range bsfs:Collection ; + bsfs:unique "false"^^xsd:boolean . + + # predicates across auxiliary node classes + bst:usedIn rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Tag ; + rdfs:range bsfs:Collection ; + bsfs:unique "false"^^xsd:boolean . + + bsc:tag rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Collection ; + rdfs:range bsfs:Tag ; + bsfs:unique "false"^^xsd:boolean . + + bst:principal rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Tag ; + rdfs:range bsfs:Node ; + bsfs:unique "true"^^xsd:boolean . + + ''') + # store migrated to the new schema + store.schema = curr + self.assertEqual(store.schema, curr) + # instances have not changed + self.assertSetEqual(set(store.graph), instances) + # add some instances of the new classes + p_partOf = curr.predicate(ns.bse.partOf) + p_shared = curr.predicate(ns.bse.shared) + p_usedIn = curr.predicate('http://bsfs.ai/schema/Tag#usedIn') + p_ctag = curr.predicate('http://bsfs.ai/schema/Collection#tag') + p_principal = curr.predicate('http://bsfs.ai/schema/Tag#principal') + store.create(curr.node(ns.bsfs.Collection), {URI('http://example.com/me/collection#1234'), URI('http://example.com/me/collection#4321')}) + # add some more triples + store.set(curr.node(ns.bsfs.Entity), ent_ids, p_shared, {True}) + store.set(curr.node(ns.bsfs.Entity), ent_ids, p_partOf, + {URI('http://example.com/me/collection#1234'), URI('http://example.com/me/collection#4321')}) + store.set(curr.node(ns.bsfs.Tag), {URI('http://example.com/me/tag#1234')}, p_usedIn, + {URI('http://example.com/me/collection#1234')}) + store.set(curr.node(ns.bsfs.Collection), {URI('http://example.com/me/collection#4321')}, p_ctag, + {URI('http://example.com/me/tag#1234'), URI('http://example.com/me/tag#4321')}) + store.set(curr.node(ns.bsfs.Tag), {URI('http://example.com/me/tag#1234')}, p_principal, + {URI('http://example.com/me/collection#1234')}) + # new instances are now in the graph + self.assertSetEqual(set(store.graph), instances | { + # collections + (rdflib.URIRef('http://example.com/me/collection#1234'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Collection)), + (rdflib.URIRef('http://example.com/me/collection#4321'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Collection)), + # partOf + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_partOf.uri), rdflib.URIRef('http://example.com/me/collection#1234')), + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_partOf.uri), rdflib.URIRef('http://example.com/me/collection#4321')), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_partOf.uri), rdflib.URIRef('http://example.com/me/collection#1234')), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_partOf.uri), rdflib.URIRef('http://example.com/me/collection#4321')), + # shared + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_shared.uri), rdflib.Literal('true', datatype=rdflib.XSD.boolean)), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_shared.uri), rdflib.Literal('true', datatype=rdflib.XSD.boolean)), + # auxiliary node connections + (rdflib.URIRef('http://example.com/me/tag#1234'), rdflib.URIRef(p_usedIn.uri), rdflib.URIRef('http://example.com/me/collection#1234')), + (rdflib.URIRef('http://example.com/me/tag#1234'), rdflib.URIRef(p_principal.uri), rdflib.URIRef('http://example.com/me/collection#1234')), + (rdflib.URIRef('http://example.com/me/collection#4321'), rdflib.URIRef(p_ctag.uri), rdflib.URIRef('http://example.com/me/tag#1234')), + (rdflib.URIRef('http://example.com/me/collection#4321'), rdflib.URIRef(p_ctag.uri), rdflib.URIRef('http://example.com/me/tag#4321')), + }) + + + # remove some classes from the schema + curr = _schema.Schema.from_string(''' + prefix rdfs: + prefix xsd: + prefix bsfs: + prefix bse: + prefix bst: + + bsfs:Entity rdfs:subClassOf bsfs:Node . + bsfs:Tag rdfs:subClassOf bsfs:Node . + bsfs:User rdfs:subClassOf bsfs:Node . + + xsd:boolean rdfs:subClassOf bsfs:Literal . + xsd:integer rdfs:subClassOf bsfs:Literal . + + bse:filesize rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity ; + rdfs:range xsd:integer ; + bsfs:unique "true"^^xsd:boolean . + + bse:tag rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity ; + rdfs:range bsfs:Tag ; + bsfs:unique "false"^^xsd:boolean . + + bse:shared rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity ; + rdfs:range xsd:boolean ; + bsfs:unique "true"^^xsd:boolean . + + bst:principal rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Tag ; + rdfs:range bsfs:Node ; + bsfs:unique "true"^^xsd:boolean . + + # removed: bsfs:Collection + # removed: xsd:string + # removed: bse:comment (bsfs:Entity -> xsd:string) + # removed: bse:partOf (bsfs:Entity -> bsfs:Collection) + # removed: bse:author (bsfs:entity -> bsfs:User) + # removed: bst:usedIn (bsfs:Tag -> bsfs:Collection) + # removed: bsc:tag (bsfs:Collection -> bsfs:Tag) + + ''') + # store migrated to the new schema + store.schema = curr + self.assertEqual(store.schema, curr) + # instances of old classes were removed + self.assertSetEqual(set(store.graph), { + # node instances + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Entity)), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Entity)), + (rdflib.URIRef('http://example.com/me/tag#1234'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Tag)), + (rdflib.URIRef('http://example.com/me/tag#4321'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Tag)), + (rdflib.URIRef('http://example.com/me'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.User)), + # filesize + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_filesize.uri), rdflib.Literal('1234', datatype=rdflib.XSD.integer)), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_filesize.uri), rdflib.Literal('1234', datatype=rdflib.XSD.integer)), + # tags + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_tag.uri), rdflib.URIRef('http://example.com/me/tag#1234')), + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_tag.uri), rdflib.URIRef('http://example.com/me/tag#4321')), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_tag.uri), rdflib.URIRef('http://example.com/me/tag#1234')), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_tag.uri), rdflib.URIRef('http://example.com/me/tag#4321')), + # shared + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_shared.uri), rdflib.Literal('true', datatype=rdflib.XSD.boolean)), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_shared.uri), rdflib.Literal('true', datatype=rdflib.XSD.boolean)), + }) + + # can only assign schema instances + self.assertRaises(TypeError, setattr, store, 'schema', None) + self.assertRaises(TypeError, setattr, store, 'schema', 1234) + self.assertRaises(TypeError, setattr, store, 'schema', 'foo') + class Foo(): pass + self.assertRaises(TypeError, setattr, store, 'schema', Foo()) + + # cannot migrate to incompatible schema + invalid = _schema.Schema.from_string(''' + prefix rdfs: + prefix xsd: + prefix bsfs: + prefix bse: + + bsfs:Entity rdfs:subClassOf bsfs:Node . + bsfs:Tag rdfs:subClassOf bsfs:Entity . # inconsistent with previous tag definition + + bse:tag rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity ; + rdfs:range bsfs:Tag ; + bsfs:unique "false"^^xsd:boolean . + + ''') + self.assertRaises(errors.ConsistencyError, setattr, store, 'schema', invalid) + invalid = _schema.Schema.from_string(''' + prefix rdfs: + prefix xsd: + prefix bsfs: + prefix bse: + + bsfs:Entity rdfs:subClassOf bsfs:Node . + bsfs:User rdfs:subClassOf bsfs:Node . + + # inconsistent predicate + bse:tag rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity ; + rdfs:range bsfs:User; + bsfs:unique "false"^^xsd:boolean . + + ''') + self.assertRaises(errors.ConsistencyError, setattr, store, 'schema', invalid) + + + def test_transaction(self): + # store setup + store = SparqlStore(None) + store.schema = self.schema + p_tag = store.schema.predicate(ns.bse.tag) + p_filesize = store.schema.predicate(ns.bse.filesize) + # prepare node types + ent_type = store.schema.node(ns.bsfs.Entity) + tag_type = store.schema.node(ns.bsfs.Tag) + ent_ids = {URI('http://example.com/me/entity#1234'), URI('http://example.com/me/entity#4321')} + tag_ids = {URI('http://example.com/me/tag#1234'), URI('http://example.com/me/tag#4321')} + # target instances + instances = { + # node instances + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Entity)), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Entity)), + (rdflib.URIRef('http://example.com/me/tag#1234'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Tag)), + (rdflib.URIRef('http://example.com/me/tag#4321'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Tag)), + # links + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_tag.uri), rdflib.URIRef('http://example.com/me/tag#1234')), + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_tag.uri), rdflib.URIRef('http://example.com/me/tag#4321')), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_tag.uri), rdflib.URIRef('http://example.com/me/tag#1234')), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_tag.uri), rdflib.URIRef('http://example.com/me/tag#4321')), + } + + # add some data + store.create(ent_type, ent_ids) + store.create(tag_type, tag_ids) + store.set(ent_type, ent_ids, p_tag, tag_ids) + store.set(ent_type, ent_ids, p_filesize, {1234}) + # current transaction is visible + self.assertSetEqual(set(store.graph), instances | { + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_filesize.uri), rdflib.Literal(1234, datatype=rdflib.XSD.integer)), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_filesize.uri), rdflib.Literal(1234, datatype=rdflib.XSD.integer)), + }) + + # rollback undoes previous changes + store.rollback() + self.assertSetEqual(set(store.graph), set()) + + # add some data once more + store.create(ent_type, ent_ids) + store.create(tag_type, tag_ids) + store.set(ent_type, ent_ids, p_tag, tag_ids) + store.set(ent_type, ent_ids, p_filesize, {1234}) + # current transaction is visible + self.assertSetEqual(set(store.graph), instances | { + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_filesize.uri), rdflib.Literal(1234, datatype=rdflib.XSD.integer)), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_filesize.uri), rdflib.Literal(1234, datatype=rdflib.XSD.integer)), + }) + + # commit saves changes + store.commit() + self.assertSetEqual(set(store.graph), instances | { + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_filesize.uri), rdflib.Literal(1234, datatype=rdflib.XSD.integer)), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_filesize.uri), rdflib.Literal(1234, datatype=rdflib.XSD.integer)), + }) + + # add additional data + store.create(ent_type, {URI('http://example.com/me/entity#hello')}) + store.set(ent_type, {URI('http://example.com/me/entity#hello')}, p_tag, tag_ids) + store.set(ent_type, ent_ids, p_filesize, {4321}) + self.assertSetEqual(set(store.graph), instances | { + (rdflib.URIRef('http://example.com/me/entity#hello'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Entity)), + (rdflib.URIRef('http://example.com/me/entity#hello'), rdflib.URIRef(p_tag.uri), rdflib.URIRef('http://example.com/me/tag#1234')), + (rdflib.URIRef('http://example.com/me/entity#hello'), rdflib.URIRef(p_tag.uri), rdflib.URIRef('http://example.com/me/tag#4321')), + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_filesize.uri), rdflib.Literal(4321, datatype=rdflib.XSD.integer)), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_filesize.uri), rdflib.Literal(4321, datatype=rdflib.XSD.integer)), + }) + + # rollback undoes only changes since last commit + store.rollback() + self.assertSetEqual(set(store.graph), instances | { + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_filesize.uri), rdflib.Literal(1234, datatype=rdflib.XSD.integer)), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_filesize.uri), rdflib.Literal(1234, datatype=rdflib.XSD.integer)), + }) + + + def test_exists(self): + # store setup + store = SparqlStore(None) + store.schema = self.schema + # prepare node types + ent_type = store.schema.node(ns.bsfs.Entity) + tag_type = store.schema.node(ns.bsfs.Tag) + # create node instances + ent_ids = { + URI('http://example.com/me/entity#1234'), + URI('http://example.com/me/entity#4321'), + } + tag_ids = { + URI('http://example.com/me/tag#1234'), + URI('http://example.com/me/tag#4321'), + } + store.create(ent_type, ent_ids) + store.create(tag_type, tag_ids) + + # exists returns all existing nodes of the correct type + self.assertSetEqual(ent_ids, store.exists(ent_type, ent_ids)) + self.assertSetEqual(tag_ids, store.exists(tag_type, tag_ids)) + # exists returns only nodes that match the type + self.assertSetEqual(set(), store.exists(ent_type, tag_ids)) + self.assertSetEqual({URI('http://example.com/me/entity#1234')}, store.exists(ent_type, { + URI('http://example.com/me/tag#1234'), + URI('http://example.com/me/entity#1234'), + })) + # exists returns only nodes that exist + self.assertSetEqual(set(), store.exists(ent_type, { + URI('http://example.com/me/entity#foo'), + URI('http://example.com/me/entity#bar'), + })) + self.assertSetEqual({URI('http://example.com/me/entity#1234')}, store.exists(ent_type, { + URI('http://example.com/me/entity#foo'), + URI('http://example.com/me/entity#1234'), + })) + + + def test_create(self): + # setup + store = SparqlStore(None) + store.schema = self.schema + + # node type must be valid + self.assertRaises(errors.ConsistencyError, store.create, self.schema.node(ns.bsfs.Entity).get_child(ns.bsfs.invalid), { + URI('http://example.com/me/entity#1234'), URI('http://example.com/me/entity#4321')}) + + # can create some nodes + ent_type = store.schema.node(ns.bsfs.Entity) + store.create(ent_type, {URI('http://example.com/me/entity#1234'), URI('http://example.com/me/entity#4321')}) + self.assertSetEqual(set(store.graph), { + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Entity)), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Entity)), + }) + + # existing nodes are skipped + store.create(ent_type, {URI('http://example.com/me/entity#1234'), URI('http://example.com/me/entity#5678')}) + self.assertSetEqual(set(store.graph), { + # previous triples + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Entity)), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Entity)), + # new triples + (rdflib.URIRef('http://example.com/me/entity#5678'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Entity)), + }) + + # can create nodes of a different type + tag_type = store.schema.node(ns.bsfs.Tag) + store.create(tag_type, {URI('http://example.com/me/tag#1234'), URI('http://example.com/me/tag#4321')}) + self.assertSetEqual(set(store.graph), { + # previous triples + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Entity)), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Entity)), + (rdflib.URIRef('http://example.com/me/entity#5678'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Entity)), + # new triples + (rdflib.URIRef('http://example.com/me/tag#1234'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Tag)), + (rdflib.URIRef('http://example.com/me/tag#4321'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Tag)), + }) + + # creation does not change types of existing nodes + tag_type = store.schema.node(ns.bsfs.Tag) + store.create(tag_type, {URI('http://example.com/me/entity#1234'), URI('http://example.com/me/entity#4321')}) + self.assertSetEqual(set(store.graph), { + # previous triples + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Entity)), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Entity)), + (rdflib.URIRef('http://example.com/me/entity#5678'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Entity)), + # new triples + (rdflib.URIRef('http://example.com/me/tag#1234'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Tag)), + (rdflib.URIRef('http://example.com/me/tag#4321'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Tag)), + }) + + + def test_set(self): + # store setup + store = SparqlStore(None) + store.schema = self.schema + # prepare node types + ent_type = store.schema.node(ns.bsfs.Entity) + user_type = store.schema.node(ns.bsfs.User) + tag_type = store.schema.node(ns.bsfs.Tag) + # prepare predicates + p_filesize = store.schema.predicate(ns.bse.filesize) + p_comment = store.schema.predicate(ns.bse.comment) + p_author = store.schema.predicate(ns.bse.author) + p_tag = store.schema.predicate(ns.bse.tag) + p_invalid = store.schema.predicate(ns.bsfs.Predicate).get_child(ns.bsfs.foo, range=store.schema.node(ns.bsfs.Tag)) + # create node instances + ent_ids = { + URI('http://example.com/me/entity#1234'), + URI('http://example.com/me/entity#4321'), + } + tag_ids = { + URI('http://example.com/me/tag#1234'), + URI('http://example.com/me/tag#4321'), + URI('http://example.com/me/tag#foo'), + URI('http://example.com/me/tag#bar'), + URI('http://example.com/me/tag#foobar'), + URI('http://example.com/me/tag#xyz'), + } + user_ids = { + URI('http://example.com/me/user#1234'), + URI('http://example.com/me/user#4321'), + } + store.create(ent_type, ent_ids) + store.create(tag_type, tag_ids) + store.create(user_type, user_ids) + + # invalid node_type is not permitted + self.assertRaises(errors.ConsistencyError, store.set, self.schema.node(ns.bsfs.Node).get_child(ns.bse.foo), + ent_ids, p_comment, {'hello world'}) + + # invalid predicate is not permitted + self.assertRaises(errors.ConsistencyError, store.set, ent_type, ent_ids, p_invalid, {'http://example.com/me/tag#1234'}) + + # predicate must match node_type + self.assertRaises(errors.ConsistencyError, store.set, tag_type, tag_ids, p_filesize, {1234}) + + # empty value does not change the graph + plen = len(store.graph) + store.set(ent_type, ent_ids, p_filesize, []) + store.set(ent_type, ent_ids, p_comment, []) + store.set(ent_type, ent_ids, p_author, []) + store.set(ent_type, ent_ids, p_tag, []) + self.assertEqual(plen, len(store.graph)) + + # cannot set multiple values on unique predicates + self.assertRaises(ValueError, store.set, ent_type, ent_ids, p_filesize, {1234, 4321}) + self.assertRaises(ValueError, store.set, ent_type, ent_ids, p_author, {URI('http://example.com/me/user#1234'), URI('http://example.com/me/user#4321')}) + + # value nodes must exist + self.assertRaises(errors.InstanceError, store.set, ent_type, ent_ids, p_author, {URI('http://example.com/me/user#invalid')}) + self.assertRaises(errors.InstanceError, store.set, ent_type, ent_ids, p_tag, {URI('http://example.com/me/tag#invalid')}) + + # value node types must be consistent with the predicate + self.assertRaises(errors.InstanceError, store.set, ent_type, ent_ids, p_author, {URI('http://example.com/me/entity#1234')}) + self.assertRaises(errors.InstanceError, store.set, ent_type, ent_ids, p_tag, {URI('http://example.com/me/entity#1234')}) + + # all value nodes must exist and be consistent + self.assertRaises(errors.InstanceError, store.set, ent_type, ent_ids, p_tag, { + URI('http://example.com/me/tag#1234'), URI('http://example.com/me/tag#invalid'), URI('http://example.com/me/entity#1234')}) + + + # set unique literal + store.set(ent_type, ent_ids, p_filesize, {1234}) + self.assertIn( + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_filesize.uri), rdflib.Literal('1234', datatype=rdflib.XSD.integer)), + set(store.graph)) + self.assertIn( + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_filesize.uri), rdflib.Literal('1234', datatype=rdflib.XSD.integer)), + set(store.graph)) + # re-assigning the same node changes nothing + store.set(ent_type, ent_ids, p_filesize, {1234}) + self.assertIn( + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_filesize.uri), rdflib.Literal('1234', datatype=rdflib.XSD.integer)), + set(store.graph)) + self.assertIn( + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_filesize.uri), rdflib.Literal('1234', datatype=rdflib.XSD.integer)), + set(store.graph)) + # cannot set multiple unique literals + self.assertRaises(ValueError, store.set, ent_type, ent_ids, p_filesize, {1234, 4321}) # same test as above + # unique literals are overwritten by set + store.set(ent_type, ent_ids, p_filesize, {4321}) + self.assertIn( + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_filesize.uri), rdflib.Literal('4321', datatype=rdflib.XSD.integer)), + set(store.graph)) + self.assertNotIn( + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_filesize.uri), rdflib.Literal('1234', datatype=rdflib.XSD.integer)), + set(store.graph)) + self.assertIn( + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_filesize.uri), rdflib.Literal('4321', datatype=rdflib.XSD.integer)), + set(store.graph)) + self.assertNotIn( + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_filesize.uri), rdflib.Literal('1234', datatype=rdflib.XSD.integer)), + set(store.graph)) + + # set non-unique literal + store.set(ent_type, ent_ids, p_comment, {'foobar'}) + self.assertTrue(set(store.graph).issuperset({ + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_comment.uri), rdflib.Literal('foobar', datatype=rdflib.XSD.string)), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_comment.uri), rdflib.Literal('foobar', datatype=rdflib.XSD.string)), + })) + # re-assigning the same node changes nothing + store.set(ent_type, ent_ids, p_comment, {'foobar'}) + self.assertTrue(set(store.graph).issuperset({ + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_comment.uri), rdflib.Literal('foobar', datatype=rdflib.XSD.string)), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_comment.uri), rdflib.Literal('foobar', datatype=rdflib.XSD.string)), + })) + # can set multiple non-unique literals at once + store.set(ent_type, ent_ids, p_comment, {'foo', 'bar'}) + self.assertTrue(set(store.graph).issuperset({ + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_comment.uri), rdflib.Literal('foo', datatype=rdflib.XSD.string)), + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_comment.uri), rdflib.Literal('bar', datatype=rdflib.XSD.string)), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_comment.uri), rdflib.Literal('foo', datatype=rdflib.XSD.string)), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_comment.uri), rdflib.Literal('bar', datatype=rdflib.XSD.string)), + })) + # non-unique literals are appended by set + store.set(ent_type, ent_ids, p_comment, {'hello world'}) + self.assertTrue(set(store.graph).issuperset({ + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_comment.uri), rdflib.Literal('foo', datatype=rdflib.XSD.string)), + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_comment.uri), rdflib.Literal('bar', datatype=rdflib.XSD.string)), + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_comment.uri), rdflib.Literal('hello world', datatype=rdflib.XSD.string)), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_comment.uri), rdflib.Literal('foo', datatype=rdflib.XSD.string)), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_comment.uri), rdflib.Literal('bar', datatype=rdflib.XSD.string)), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_comment.uri), rdflib.Literal('hello world', datatype=rdflib.XSD.string)), + })) + + # set unique node + store.set(ent_type, ent_ids, p_author, {URI('http://example.com/me/user#1234')}) + self.assertIn( + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_author.uri), rdflib.URIRef('http://example.com/me/user#1234')), + set(store.graph)) + self.assertIn( + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_author.uri), rdflib.URIRef('http://example.com/me/user#1234')), + set(store.graph)) + # re-assigning the same node changes nothing + store.set(ent_type, ent_ids, p_author, {URI('http://example.com/me/user#1234')}) + self.assertIn( + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_author.uri), rdflib.URIRef('http://example.com/me/user#1234')), + set(store.graph)) + self.assertIn( + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_author.uri), rdflib.URIRef('http://example.com/me/user#1234')), + set(store.graph)) + # cannot set multiple unique nodes + self.assertRaises(ValueError, store.set, ent_type, ent_ids, p_author, {URI('http://example.com/me/user#1234'), URI('http://example.com/me/user#4321')}) + # unique nodes are overwritten by set + store.set(ent_type, ent_ids, p_author, {URI('http://example.com/me/user#4321')}) + self.assertIn( + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_author.uri), rdflib.URIRef('http://example.com/me/user#4321')), + set(store.graph)) + self.assertNotIn( + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_author.uri), rdflib.URIRef('http://example.com/me/user#1234')), + set(store.graph)) + self.assertIn( + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_author.uri), rdflib.URIRef('http://example.com/me/user#4321')), + set(store.graph)) + self.assertNotIn( + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_author.uri), rdflib.URIRef('http://example.com/me/user#1234')), + set(store.graph)) + + # set non-unique node + store.set(ent_type, ent_ids, p_tag, {'http://example.com/me/tag#foobar'}) + self.assertTrue(set(store.graph).issuperset({ + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_tag.uri), rdflib.URIRef('http://example.com/me/tag#foobar')), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_tag.uri), rdflib.URIRef('http://example.com/me/tag#foobar')), + })) + # re-assigning the same node changes nothing + store.set(ent_type, ent_ids, p_tag, {'http://example.com/me/tag#foobar'}) + self.assertTrue(set(store.graph).issuperset({ + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_tag.uri), rdflib.URIRef('http://example.com/me/tag#foobar')), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_tag.uri), rdflib.URIRef('http://example.com/me/tag#foobar')), + })) + # can set multiple non-unique literals at once + store.set(ent_type, ent_ids, p_tag, {'http://example.com/me/tag#1234', 'http://example.com/me/tag#4321'}) + self.assertTrue(set(store.graph).issuperset({ + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_tag.uri), rdflib.URIRef('http://example.com/me/tag#1234')), + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_tag.uri), rdflib.URIRef('http://example.com/me/tag#4321')), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_tag.uri), rdflib.URIRef('http://example.com/me/tag#1234')), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_tag.uri), rdflib.URIRef('http://example.com/me/tag#4321')), + })) + # non-unique nodes are appended by set + store.set(ent_type, ent_ids, p_tag, {'http://example.com/me/tag#foo', 'http://example.com/me/tag#bar'}) + self.assertTrue(set(store.graph).issuperset({ + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_tag.uri), rdflib.URIRef('http://example.com/me/tag#1234')), + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_tag.uri), rdflib.URIRef('http://example.com/me/tag#4321')), + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_tag.uri), rdflib.URIRef('http://example.com/me/tag#foo')), + (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_tag.uri), rdflib.URIRef('http://example.com/me/tag#bar')), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_tag.uri), rdflib.URIRef('http://example.com/me/tag#1234')), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_tag.uri), rdflib.URIRef('http://example.com/me/tag#4321')), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_tag.uri), rdflib.URIRef('http://example.com/me/tag#foo')), + (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_tag.uri), rdflib.URIRef('http://example.com/me/tag#bar')), + })) + + # nothing happens when no guids are given + plen = len(store.graph) + store.set(ent_type, set(), p_comment, {'xyz'}) + store.set(ent_type, set(), p_tag, {URI('http://example.com/me/tag#xyz')}) + self.assertEqual(plen, len(store.graph)) + + # guids must be instances of node_type + self.assertRaises(errors.InstanceError, store.set, ent_type, tag_ids, p_comment, {'xyz'}) + # inexistent guids + self.assertRaises(errors.InstanceError, store.set, ent_type, {URI('http://example.com/me/entity#foobar')}, p_comment, {'xyz'}) + + +## main ## + +if __name__ == '__main__': + unittest.main() + +## EOF ## -- cgit v1.2.3 From 0e52514639b043454425a9cc2317d27e628a1027 Mon Sep 17 00:00:00 2001 From: Matthias Baumgartner Date: Sun, 18 Dec 2022 13:42:34 +0100 Subject: namespace and uri extensions --- test/namespace/test_namespace.py | 87 ++++++++++++++++++++++++++++------------ test/utils/test_uri.py | 18 +++++++++ 2 files changed, 79 insertions(+), 26 deletions(-) (limited to 'test') diff --git a/test/namespace/test_namespace.py b/test/namespace/test_namespace.py index 1ad53e3..f109653 100644 --- a/test/namespace/test_namespace.py +++ b/test/namespace/test_namespace.py @@ -5,8 +5,12 @@ A copy of the license is provided with the project. Author: Matthias Baumgartner, 2022 """ # imports +import operator import unittest +# bsfs imports +from bsfs.utils import URI + # objects to test from bsfs.namespace.namespace import Namespace, ClosedNamespace @@ -16,73 +20,104 @@ from bsfs.namespace.namespace import Namespace, ClosedNamespace class TestNamespace(unittest.TestCase): def test_essentials(self): # string conversion - self.assertEqual(str(Namespace('http://example.org/')), 'Namespace(http://example.org/)') - self.assertEqual(str(Namespace('http://example.org#')), 'Namespace(http://example.org#)') - self.assertEqual(repr(Namespace('http://example.org/')), 'Namespace(http://example.org/)') - self.assertEqual(repr(Namespace('http://example.org#')), 'Namespace(http://example.org#)') + self.assertEqual(str(Namespace('http://example.org/')), 'Namespace(http://example.org)') + self.assertEqual(str(Namespace('http://example.org#')), 'Namespace(http://example.org)') + self.assertEqual(repr(Namespace('http://example.org/')), 'Namespace(http://example.org, #, /)') + self.assertEqual(repr(Namespace('http://example.org#')), 'Namespace(http://example.org, #, /)') + self.assertEqual(repr(Namespace('http://example.org', fsep='.')), 'Namespace(http://example.org, ., /)') + self.assertEqual(repr(Namespace('http://example.org', psep='.')), 'Namespace(http://example.org, #, .)') + # repeated separators are truncated + self.assertEqual(str(Namespace('http://example.org////')), 'Namespace(http://example.org)') + self.assertEqual(str(Namespace('http://example.org####')), 'Namespace(http://example.org)') + self.assertEqual(repr(Namespace('http://example.org///##')), 'Namespace(http://example.org, #, /)') # comparison class Foo(Namespace): pass self.assertEqual(Namespace('http://example.org/'), Namespace('http://example.org/')) + self.assertEqual(Namespace('http://example.org/'), Namespace('http://example.org')) + self.assertEqual(Namespace('http://example.org/'), Namespace('http://example.org#')) + self.assertNotEqual(Namespace('http://example.org'), Namespace('http://example.org', fsep='.')) + self.assertNotEqual(Namespace('http://example.org'), Namespace('http://example.org', psep='.')) self.assertNotEqual(Namespace('http://example.org/'), Foo('http://example.org/')) self.assertNotEqual(Foo('http://example.org/'), Namespace('http://example.org/')) - self.assertNotEqual(Namespace('http://example.org/'), Namespace('http://example.org#')) - self.assertNotEqual(Namespace('http://example.org#'), Namespace('http://example.org/')) # hashing self.assertEqual(hash(Namespace('http://example.org/')), hash(Namespace('http://example.org/'))) + self.assertEqual(hash(Namespace('http://example.org/')), hash(Namespace('http://example.org'))) + self.assertEqual(hash(Namespace('http://example.org/')), hash(Namespace('http://example.org#'))) + self.assertNotEqual(hash(Namespace('http://example.org')), hash(Namespace('http://example.com'))) + self.assertNotEqual(hash(Namespace('http://example.org')), hash(Namespace('http://example.org', fsep='.'))) + self.assertNotEqual(hash(Namespace('http://example.org')), hash(Namespace('http://example.org', psep='.'))) self.assertNotEqual(hash(Namespace('http://example.org/')), hash(Foo('http://example.org/'))) self.assertNotEqual(hash(Foo('http://example.org/')), hash(Namespace('http://example.org/'))) - self.assertNotEqual(hash(Namespace('http://example.org/')), hash(Namespace('http://example.org#'))) - self.assertNotEqual(hash(Namespace('http://example.org#')), hash(Namespace('http://example.org/'))) def test_getattr(self): - self.assertEqual(Namespace('http://example.org/').foo, 'http://example.org/foo') - self.assertEqual(Namespace('http://example.org/').bar, 'http://example.org/bar') + self.assertEqual(Namespace('http://example.org/').foo, 'http://example.org#foo') + self.assertEqual(Namespace('http://example.org/').bar, 'http://example.org#bar') + self.assertEqual(Namespace('http://example.org/', fsep='/').foo, 'http://example.org/foo') + self.assertEqual(Namespace('http://example.org/', fsep='/').bar, 'http://example.org/bar') + self.assertEqual(Namespace('http://example.org', fsep='/').foo, 'http://example.org/foo') + self.assertEqual(Namespace('http://example.org', fsep='/').bar, 'http://example.org/bar') + self.assertEqual(Namespace('http://example.org#', fsep='/').foo, 'http://example.org#/foo') + self.assertEqual(Namespace('http://example.org#', fsep='/').bar, 'http://example.org#/bar') self.assertEqual(Namespace('http://example.org/me#').foo, 'http://example.org/me#foo') self.assertEqual(Namespace('http://example.org/me#').bar, 'http://example.org/me#bar') def test_getitem(self): - self.assertEqual(Namespace('http://example.org/')['foo'], 'http://example.org/foo') - self.assertEqual(Namespace('http://example.org/')['bar'], 'http://example.org/bar') + self.assertEqual(Namespace('http://example.org')['foo'], 'http://example.org#foo') + self.assertEqual(Namespace('http://example.org')['bar'], 'http://example.org#bar') + self.assertEqual(Namespace('http://example.org', fsep='/')['foo'], 'http://example.org/foo') + self.assertEqual(Namespace('http://example.org', fsep='/')['bar'], 'http://example.org/bar') self.assertEqual(Namespace('http://example.org/me#')['foo'], 'http://example.org/me#foo') self.assertEqual(Namespace('http://example.org/me#')['bar'], 'http://example.org/me#bar') + def test_add(self): + self.assertEqual(Namespace('http://example.org') + 'foo', Namespace('http://example.org/foo')) + self.assertEqual(Namespace('http://example.org', psep='.') + 'foo', Namespace('http://example.org.foo', psep='.')) + self.assertEqual(Namespace('http://example.org') + 'foo' + 'bar', Namespace('http://example.org/foo/bar')) + # can add URIs + self.assertEqual(Namespace('http://example.org') + URI('foo'), Namespace('http://example.org/foo')) + # can only add strings + self.assertRaises(TypeError, operator.add, Namespace('http://example.org'), 1234) + self.assertRaises(TypeError, operator.add, Namespace('http://example.org'), Namespace('http://example.com')) + class TestClosedNamespace(unittest.TestCase): def test_essentials(self): # string conversion - self.assertEqual(str(ClosedNamespace('http://example.org/')), 'ClosedNamespace(http://example.org/)') - self.assertEqual(str(ClosedNamespace('http://example.org#')), 'ClosedNamespace(http://example.org#)') - self.assertEqual(repr(ClosedNamespace('http://example.org/')), 'ClosedNamespace(http://example.org/)') - self.assertEqual(repr(ClosedNamespace('http://example.org#')), 'ClosedNamespace(http://example.org#)') + self.assertEqual(str(ClosedNamespace('http://example.org/')), 'ClosedNamespace(http://example.org)') + self.assertEqual(str(ClosedNamespace('http://example.org#')), 'ClosedNamespace(http://example.org)') + self.assertEqual(repr(ClosedNamespace('http://example.org/')), 'ClosedNamespace(http://example.org, #, /)') + self.assertEqual(repr(ClosedNamespace('http://example.org#')), 'ClosedNamespace(http://example.org, #, /)') + self.assertEqual(repr(ClosedNamespace('http://example.org', fsep='.')), 'ClosedNamespace(http://example.org, ., /)') + self.assertEqual(repr(ClosedNamespace('http://example.org', psep='.')), 'ClosedNamespace(http://example.org, #, .)') # comparison class Foo(ClosedNamespace): pass - self.assertEqual(ClosedNamespace('http://example.org/'), ClosedNamespace('http://example.org/')) + self.assertEqual(ClosedNamespace('http://example.org'), ClosedNamespace('http://example.org#')) + self.assertEqual(ClosedNamespace('http://example.org'), ClosedNamespace('http://example.org')) + self.assertEqual(ClosedNamespace('http://example.org'), ClosedNamespace('http://example.org/')) self.assertEqual(ClosedNamespace('http://example.org/', 'foo', 'bar'), ClosedNamespace('http://example.org/', 'foo', 'bar')) self.assertNotEqual(ClosedNamespace('http://example.org/', 'foo'), ClosedNamespace('http://example.org/', 'bar')) self.assertNotEqual(ClosedNamespace('http://example.org/'), Foo('http://example.org/')) self.assertNotEqual(Foo('http://example.org/'), ClosedNamespace('http://example.org/')) - self.assertNotEqual(ClosedNamespace('http://example.org/'), ClosedNamespace('http://example.org#')) - self.assertNotEqual(ClosedNamespace('http://example.org#'), ClosedNamespace('http://example.org/')) # hashing - self.assertEqual(hash(ClosedNamespace('http://example.org/')), hash(ClosedNamespace('http://example.org/'))) + self.assertEqual(hash(ClosedNamespace('http://example.org')), hash(ClosedNamespace('http://example.org'))) + self.assertEqual(hash(ClosedNamespace('http://example.org')), hash(ClosedNamespace('http://example.org/'))) + self.assertEqual(hash(ClosedNamespace('http://example.org')), hash(ClosedNamespace('http://example.org#'))) self.assertEqual(hash(ClosedNamespace('http://example.org/', 'foo', 'bar')), hash(ClosedNamespace('http://example.org/', 'foo', 'bar'))) self.assertNotEqual(hash(ClosedNamespace('http://example.org/', 'foo')), hash(ClosedNamespace('http://example.org/', 'bar'))) self.assertNotEqual(hash(ClosedNamespace('http://example.org/')), hash(Foo('http://example.org/'))) self.assertNotEqual(hash(Foo('http://example.org/')), hash(ClosedNamespace('http://example.org/'))) - self.assertNotEqual(hash(ClosedNamespace('http://example.org/')), hash(ClosedNamespace('http://example.org#'))) - self.assertNotEqual(hash(ClosedNamespace('http://example.org#')), hash(ClosedNamespace('http://example.org/'))) def test_getattr(self): - self.assertEqual(ClosedNamespace('http://example.org/', 'foo', 'bar').foo, 'http://example.org/foo') - self.assertEqual(ClosedNamespace('http://example.org/', 'bar', 'bar').bar, 'http://example.org/bar') + self.assertEqual(ClosedNamespace('http://example.org/', 'foo', 'bar').foo, 'http://example.org#foo') + self.assertEqual(ClosedNamespace('http://example.org/', 'bar', 'bar').bar, 'http://example.org#bar') self.assertEqual(ClosedNamespace('http://example.org/me#', 'foo', 'bar').foo, 'http://example.org/me#foo') self.assertEqual(ClosedNamespace('http://example.org/me#', 'foo', 'bar').bar, 'http://example.org/me#bar') self.assertRaises(KeyError, getattr, ClosedNamespace('http://example.org/', 'bar', 'bar'), 'foobar') self.assertRaises(KeyError, getattr, ClosedNamespace('http://example.org#', 'bar', 'bar'), 'foobar') def test_getitem(self): - self.assertEqual(ClosedNamespace('http://example.org/', 'foo', 'bar')['foo'], 'http://example.org/foo') - self.assertEqual(ClosedNamespace('http://example.org/', 'foo', 'bar')['bar'], 'http://example.org/bar') + self.assertEqual(ClosedNamespace('http://example.org/', 'foo', 'bar')['foo'], 'http://example.org#foo') + self.assertEqual(ClosedNamespace('http://example.org/', 'foo', 'bar')['bar'], 'http://example.org#bar') self.assertEqual(ClosedNamespace('http://example.org/me#', 'foo', 'bar')['foo'], 'http://example.org/me#foo') self.assertEqual(ClosedNamespace('http://example.org/me#', 'foo', 'bar')['bar'], 'http://example.org/me#bar') self.assertRaises(KeyError, ClosedNamespace('http://example.org/', 'bar', 'bar').__getitem__, 'foobar') diff --git a/test/utils/test_uri.py b/test/utils/test_uri.py index 976e75d..770e65a 100644 --- a/test/utils/test_uri.py +++ b/test/utils/test_uri.py @@ -5,6 +5,7 @@ A copy of the license is provided with the project. Author: Matthias Baumgartner, 2022 """ # imports +import operator import unittest # objects to test @@ -161,6 +162,23 @@ class TestURI(unittest.TestCase): # empty URI self.assertRaises(ValueError, getattr, URI(''), 'fragment') + def test_overloaded(self): + # composition + self.assertIsInstance(URI('http://user@www.example.com:1234/{}/path1?{}#fragment') + 'hello', URI) + self.assertIsInstance(URI('http://user@www.example.com:1234/{}/path1?{}#fragment') * 2, URI) + self.assertIsInstance(2 * URI('http://user@www.example.com:1234/{}/path1?{}#fragment'), URI) # rmul + self.assertIsInstance(URI('http://user@www.example.com:1234/{}/path1?{}#fragment').join(['hello', 'world']) , URI) + # stripping + self.assertIsInstance(URI('http://user@www.example.com:1234/path0/path1?query#fragment').strip(), URI) + self.assertIsInstance(URI('http://user@www.example.com:1234/path0/path1?query#fragment').lstrip(), URI) + self.assertIsInstance(URI('http://user@www.example.com:1234/path0/path1?query#fragment').rstrip(), URI) + # case fold + self.assertIsInstance(URI('http://user@www.example.com:1234/path0/path1?query#fragment').lower(), URI) + self.assertIsInstance(URI('http://user@www.example.com:1234/path0/path1?query#fragment').upper(), URI) + # formatting + self.assertIsInstance(URI('http://user@www.example.com:1234/{}/path1?{}#fragment').format('hello', 'world'), URI) + self.assertIsInstance(URI('http://user@www.example.com:1234/%s/path1?%s#fragment') % ('hello', 'world'), URI) + self.assertIsInstance(URI('http://user@www.example.com:1234/path0/path1?query#fragment').replace('path0', 'pathX'), URI) ## main ## -- cgit v1.2.3 From 1b570e45a4e99a4e7f9ad9d01b4fa93e38fbff38 Mon Sep 17 00:00:00 2001 From: Matthias Baumgartner Date: Sun, 18 Dec 2022 13:45:35 +0100 Subject: schema ordering --- test/schema/test_schema.py | 129 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) (limited to 'test') diff --git a/test/schema/test_schema.py b/test/schema/test_schema.py index 2dc26e8..888cdca 100644 --- a/test/schema/test_schema.py +++ b/test/schema/test_schema.py @@ -206,6 +206,135 @@ class TestSchema(unittest.TestCase): self.assertNotEqual(hash(schema), hash(Schema(self.predicates + [self.p_root.get_child(ns.bse.filesize, self.n_ent, self.l_integer)], self.nodes, self.literals))) + def test_order(self): + # setup + class Foo(): pass + p_foo = self.p_root.get_child(ns.bse.foo, self.n_ent, self.l_string, True) + p_sub = p_foo.get_child(ns.bse.sub, self.n_ent, self.l_string, True) + p_bar = self.p_root.get_child(ns.bse.bar, self.n_ent, self.l_string, True) + + # can only compare schema to other schema + # < + self.assertRaises(TypeError, operator.lt, Schema({p_foo, p_bar}), 'hello world') + self.assertRaises(TypeError, operator.lt, Schema({p_foo, p_bar}), 1234) + self.assertRaises(TypeError, operator.lt, Schema({p_foo, p_bar}), p_foo) + self.assertRaises(TypeError, operator.lt, Schema({p_foo, p_bar}), Foo()) + # <= + self.assertRaises(TypeError, operator.le, Schema({p_foo, p_bar}), 'hello world') + self.assertRaises(TypeError, operator.le, Schema({p_foo, p_bar}), 1234) + self.assertRaises(TypeError, operator.le, Schema({p_foo, p_bar}), p_foo) + self.assertRaises(TypeError, operator.le, Schema({p_foo, p_bar}), Foo()) + # > + self.assertRaises(TypeError, operator.gt, Schema({p_foo, p_bar}), 'hello world') + self.assertRaises(TypeError, operator.gt, Schema({p_foo, p_bar}), 1234) + self.assertRaises(TypeError, operator.gt, Schema({p_foo, p_bar}), p_foo) + self.assertRaises(TypeError, operator.gt, Schema({p_foo, p_bar}), Foo()) + # >= + self.assertRaises(TypeError, operator.ge, Schema({p_foo, p_bar}), 'hello world') + self.assertRaises(TypeError, operator.ge, Schema({p_foo, p_bar}), 1234) + self.assertRaises(TypeError, operator.ge, Schema({p_foo, p_bar}), p_foo) + self.assertRaises(TypeError, operator.ge, Schema({p_foo, p_bar}), Foo()) + + # a schema is a subset of itself + self.assertTrue(operator.le(Schema({self.p_tag}), Schema({self.p_tag}))) + # a schema is a superset of itself + self.assertTrue(operator.ge(Schema({self.p_tag}), Schema({self.p_tag}))) + # a schema is not a true subset of itself + self.assertFalse(operator.lt(Schema({self.p_tag}), Schema({self.p_tag}))) + # a schema is not a true superset of itself + self.assertFalse(operator.gt(Schema({self.p_tag}), Schema({self.p_tag}))) + + # subset considers predicates + self.assertTrue(operator.lt(Schema({p_foo}), Schema({p_foo, p_bar}))) + self.assertTrue(operator.lt(Schema({p_foo}), Schema({p_sub}))) + self.assertFalse(operator.lt(Schema({p_foo}), Schema({p_bar}))) + # subset considers nodes + self.assertTrue(operator.lt(Schema({self.p_tag}), Schema({self.p_tag}, {self.n_unused}))) + self.assertFalse(operator.lt(Schema({self.p_tag}, {self.n_unused}), Schema({self.p_tag}))) + # subset considers literals + self.assertTrue(operator.lt(Schema({self.p_tag}), Schema({self.p_tag}, {}, {self.l_unused}))) + self.assertFalse(operator.lt(Schema({self.p_tag}, {}, {self.l_unused}), Schema({self.p_tag}))) + # subset considers differences in predicates and nodes + self.assertTrue(operator.lt(Schema({self.p_tag}), Schema({self.p_group}))) + self.assertTrue(operator.le(Schema({self.p_tag}), Schema({self.p_group}))) + # subset considers differences in predicates and literals + self.assertTrue(operator.lt(Schema.Empty(), Schema({self.p_comment}))) + # subset considers differences in predicates, nodes, and literals + self.assertTrue(operator.lt(Schema({}), Schema.Empty())) + self.assertTrue(operator.lt(Schema({self.p_tag}), Schema.from_string(self.schema_str))) + self.assertTrue(operator.le(Schema({self.p_tag}), Schema.from_string(self.schema_str))) + self.assertFalse(operator.lt(Schema({self.p_comment}), Schema({self.p_tag}))) + self.assertFalse(operator.le(Schema({self.p_comment}), Schema({self.p_tag}))) + + # superset considers predicates + self.assertTrue(operator.gt(Schema({p_foo, p_bar}), Schema({p_foo}))) + self.assertTrue(operator.gt(Schema({p_sub}), Schema({p_foo}))) + self.assertFalse(operator.gt(Schema({p_foo}), Schema({p_bar}))) + # superset considers nodes + self.assertTrue(operator.gt(Schema({self.p_tag}, {self.n_unused}), Schema({self.p_tag}))) + self.assertFalse(operator.gt(Schema({self.p_tag}), Schema({self.p_tag}, {self.n_unused}))) + # superset considers literals + self.assertTrue(operator.gt(Schema({self.p_tag}, {}, {self.l_unused}), Schema({self.p_tag}))) + self.assertFalse(operator.gt(Schema({self.p_tag}), Schema({self.p_tag}, {}, {self.l_unused}))) + # superset considers differences in predicates and nodes + self.assertTrue(operator.gt(Schema({self.p_group}), Schema({self.p_tag}))) + self.assertTrue(operator.ge(Schema({self.p_group}), Schema({self.p_tag}))) + # superset considers differences in predicates and literals + self.assertTrue(operator.gt(Schema({self.p_comment}), Schema.Empty())) + # superset considers differences in predicates, nodes, and literals + self.assertTrue(operator.gt(Schema.Empty(), Schema({}))) + self.assertTrue(operator.gt(Schema.from_string(self.schema_str), Schema({self.p_tag}))) + self.assertTrue(operator.ge(Schema.from_string(self.schema_str), Schema({self.p_tag}))) + self.assertFalse(operator.gt(Schema({self.p_tag}), Schema({self.p_comment}))) + self.assertFalse(operator.ge(Schema({self.p_tag}), Schema({self.p_comment}))) + + # inconsistent schema cannot be a subset + self.assertFalse(operator.le(Schema({p_foo}), Schema({ + self.p_root.get_child(ns.bse.foo, self.n_ent, self.l_integer, True)}))) # inconsistent w.r.t. literal + self.assertFalse(operator.le(Schema({p_foo}), Schema({ + self.p_root.get_child(ns.bse.foo, self.n_img, self.l_string, True)}))) # inconsistent w.r.t. node + self.assertFalse(operator.le(Schema({p_foo}), Schema({ + self.p_root.get_child(ns.bse.foo, self.n_ent, self.l_string, False)}))) # inconsistent w.r.t. unique + self.assertFalse(operator.le(Schema({}, {self.n_img}), Schema({}, { + types.Node(ns.bsfs.Image, types.Node(ns.bsfs.Node, None))}))) + self.assertFalse(operator.le(Schema({}, {}, {self.l_integer}), Schema({}, {}, { + types.Literal(ns.xsd.integer, types.Literal(ns.xsd.number, types.Literal(ns.bsfs.Literal, None)))}))) + # inconsistent schema cannot be a true subset + self.assertFalse(operator.lt(Schema({p_foo}), Schema({ + self.p_root.get_child(ns.bse.foo, self.n_ent, self.l_integer, True)}))) # inconsistent w.r.t. literal + self.assertFalse(operator.lt(Schema({p_foo}), Schema({ + self.p_root.get_child(ns.bse.foo, self.n_img, self.l_string, True)}))) # inconsistent w.r.t. node + self.assertFalse(operator.lt(Schema({p_foo}), Schema({ + self.p_root.get_child(ns.bse.foo, self.n_ent, self.l_string, False)}))) # inconsistent w.r.t. unique + self.assertFalse(operator.lt(Schema({}, {self.n_img}), Schema({}, { + types.Node(ns.bsfs.Image, types.Node(ns.bsfs.Node, None))}))) + self.assertFalse(operator.lt(Schema({}, {}, {self.l_integer}), Schema({}, {}, { + types.Literal(ns.xsd.integer, types.Literal(ns.xsd.number, types.Literal(ns.bsfs.Literal, None)))}))) + # inconsistent schema cannot be a superset + self.assertFalse(operator.ge(Schema({p_foo}), Schema({ + self.p_root.get_child(ns.bse.foo, self.n_ent, self.l_integer, True)}))) # inconsistent w.r.t. literal + self.assertFalse(operator.ge(Schema({p_foo}), Schema({ + self.p_root.get_child(ns.bse.foo, self.n_img, self.l_string, True)}))) # inconsistent w.r.t. node + self.assertFalse(operator.ge(Schema({p_foo}), Schema({ + self.p_root.get_child(ns.bse.foo, self.n_ent, self.l_string, False)}))) # inconsistent w.r.t. unique + self.assertFalse(operator.ge(Schema({}, {self.n_img}), Schema({}, { + types.Node(ns.bsfs.Image, types.Node(ns.bsfs.Node, None))}))) + self.assertFalse(operator.ge(Schema({}, {}, {self.l_integer}), Schema({}, {}, { + types.Literal(ns.xsd.integer, types.Literal(ns.xsd.number, types.Literal(ns.bsfs.Literal, None)))}))) + # inconsistent schema cannot be a true superset + self.assertFalse(operator.gt(Schema({p_foo}), Schema({ + self.p_root.get_child(ns.bse.foo, self.n_ent, self.l_integer, True)}))) # inconsistent w.r.t. literal + self.assertFalse(operator.gt(Schema({p_foo}), Schema({ + self.p_root.get_child(ns.bse.foo, self.n_img, self.l_string, True)}))) # inconsistent w.r.t. node + self.assertFalse(operator.gt(Schema({p_foo}), Schema({ + self.p_root.get_child(ns.bse.foo, self.n_ent, self.l_string, False)}))) # inconsistent w.r.t. unique + self.assertFalse(operator.gt(Schema({}, {self.n_img}), Schema({}, { + types.Node(ns.bsfs.Image, types.Node(ns.bsfs.Node, None))}))) + self.assertFalse(operator.gt(Schema({}, {}, {self.l_integer}), Schema({}, {}, { + types.Literal(ns.xsd.integer, types.Literal(ns.xsd.number, types.Literal(ns.bsfs.Literal, None)))}))) + + + def test_diff(self): # difference can be empty diff = Schema({self.p_tag}).diff(Schema({self.p_group})) -- cgit v1.2.3 From ebc3ccb5fdce950649bfcbf18f88ecb4a9dbcad0 Mon Sep 17 00:00:00 2001 From: Matthias Baumgartner Date: Sun, 18 Dec 2022 13:53:34 +0100 Subject: import fixes --- test/graph/test_graph.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'test') diff --git a/test/graph/test_graph.py b/test/graph/test_graph.py index 96046ef..eaeee0c 100644 --- a/test/graph/test_graph.py +++ b/test/graph/test_graph.py @@ -8,10 +8,10 @@ Author: Matthias Baumgartner, 2022 import unittest # bsie imports -from bsfs import schema as _schema +from bsfs import schema from bsfs.namespace import ns from bsfs.triple_store import SparqlStore -from bsfs.utils import URI +from bsfs.utils import URI, errors from bsfs.graph.nodes import Nodes # objects to test @@ -24,7 +24,7 @@ class TestGraph(unittest.TestCase): def setUp(self): self.user = URI('http://example.com/me') self.backend = SparqlStore.Open(None) - self.backend.schema = _schema.Schema.from_string(''' + self.backend.schema = schema.Schema.from_string(''' prefix rdfs: prefix bsfs: bsfs:Entity rdfs:subClassOf bsfs:Node . -- cgit v1.2.3 From edd5390b6db1550f6a80a46f0eaf5f3916997532 Mon Sep 17 00:00:00 2001 From: Matthias Baumgartner Date: Sun, 18 Dec 2022 14:06:58 +0100 Subject: information hiding --- test/graph/test_nodes.py | 54 +++++++++++++------------- test/triple_store/test_sparql.py | 84 ++++++++++++++++++++-------------------- 2 files changed, 69 insertions(+), 69 deletions(-) (limited to 'test') diff --git a/test/graph/test_nodes.py b/test/graph/test_nodes.py index 1fbd1e5..71fbb10 100644 --- a/test/graph/test_nodes.py +++ b/test/graph/test_nodes.py @@ -157,10 +157,10 @@ class TestNodes(unittest.TestCase): # missing nodes are created self.assertSetEqual(self.ent_ids, nodes._ensure_nodes(self.ent_type, self.ent_ids)) # get creation time from backend manually - time_triples = list(self.backend.graph.objects(rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(self.t_created.uri))) + 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), { # 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')), @@ -171,7 +171,7 @@ 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), { # 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')), @@ -183,10 +183,10 @@ class TestNodes(unittest.TestCase): # type and guids don't need to match the node instance's members self.assertSetEqual(self.tag_ids, nodes._ensure_nodes(self.tag_type, self.tag_ids)) # 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))) + 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), { # 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')), @@ -202,7 +202,7 @@ class TestNodes(unittest.TestCase): 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()) + self.assertSetEqual(set(self.backend._graph), set()) set_ = nodes._Nodes__set # node_type must match predicate's domain @@ -214,10 +214,10 @@ class TestNodes(unittest.TestCase): # set literal value set_(self.p_filesize.uri, 1234) # get creation time from backend manually - time_triples = list(self.backend.graph.objects(rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(self.t_created.uri))) + 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), { # 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')), @@ -233,10 +233,10 @@ class TestNodes(unittest.TestCase): tags = Nodes(self.backend, self.user, 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))) + 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), { # 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')), @@ -265,11 +265,11 @@ class TestNodes(unittest.TestCase): Nodes(self.backend, self.user, self.ent_type, self.ent_ids)) def test_set(self): - self.assertSetEqual(set(self.backend.graph), set()) + self.assertSetEqual(set(self.backend._graph), set()) nodes = Nodes(self.backend, self.user, 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({ + 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')), @@ -279,7 +279,7 @@ class TestNodes(unittest.TestCase): })) # 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.assertTrue(set(self.backend.graph).issuperset({ + 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')), @@ -293,33 +293,33 @@ class TestNodes(unittest.TestCase): })) # cannot set protected predicate - curr = set(self.backend.graph) + curr = set(self.backend._graph) self.assertRaises(errors.PermissionDeniedError, nodes.set, self.t_created.uri, 12345) - self.assertSetEqual(curr, set(self.backend.graph)) + self.assertSetEqual(curr, set(self.backend._graph)) # predicate.domain must match node_type self.assertRaises(errors.ConsistencyError, nodes.set, self.p_representative.uri, nodes) - self.assertSetEqual(curr, set(self.backend.graph)) + self.assertSetEqual(curr, set(self.backend._graph)) # value's node_type must match predicate's range self.assertRaises(errors.ConsistencyError, nodes.set, self.p_tag.uri, nodes) - self.assertSetEqual(curr, set(self.backend.graph)) + self.assertSetEqual(curr, set(self.backend._graph)) # value type must match predicate's range type self.assertRaises(TypeError, nodes.set, self.p_tag.uri, 'invalid') - self.assertSetEqual(curr, set(self.backend.graph)) + 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')})) - self.assertSetEqual(curr, set(self.backend.graph)) + self.assertSetEqual(curr, set(self.backend._graph)) def test_set_from_iterable(self): - self.assertSetEqual(set(self.backend.graph), set()) + self.assertSetEqual(set(self.backend._graph), set()) nodes = Nodes(self.backend, self.user, 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), }.items())) - self.assertTrue(set(self.backend.graph).issuperset({ + 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')), @@ -335,22 +335,22 @@ class TestNodes(unittest.TestCase): })) # cannot set protected predicate - curr = set(self.backend.graph) + curr = set(self.backend._graph) self.assertRaises(errors.PermissionDeniedError, nodes.set_from_iterable, ((self.p_filesize.uri, 1234), (self.t_created.uri, 12345))) - self.assertSetEqual(curr, set(self.backend.graph)) + self.assertSetEqual(curr, set(self.backend._graph)) # predicate.domain must match node_type self.assertRaises(errors.ConsistencyError, nodes.set_from_iterable, ((self.p_filesize.uri, 1234), (self.p_representative.uri, nodes))) - self.assertSetEqual(curr, set(self.backend.graph)) + self.assertSetEqual(curr, set(self.backend._graph)) # value's node_type must match predicate's range self.assertRaises(errors.ConsistencyError, nodes.set_from_iterable, ((self.p_filesize.uri, 1234), (self.p_tag.uri, nodes))) - self.assertSetEqual(curr, set(self.backend.graph)) + self.assertSetEqual(curr, set(self.backend._graph)) # value type must match predicate's range type self.assertRaises(TypeError, nodes.set_from_iterable, ((self.p_filesize.uri, 1234), (self.p_tag.uri, 'invalid'))) - self.assertSetEqual(curr, set(self.backend.graph)) + 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.assertSetEqual(curr, set(self.backend.graph)) + self.assertSetEqual(curr, set(self.backend._graph)) ## main ## diff --git a/test/triple_store/test_sparql.py b/test/triple_store/test_sparql.py index f047235..ecf3a07 100644 --- a/test/triple_store/test_sparql.py +++ b/test/triple_store/test_sparql.py @@ -179,7 +179,7 @@ class TestSparqlStore(unittest.TestCase): (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_author.uri), rdflib.URIRef('http://example.com/me')), (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_author.uri), rdflib.URIRef('http://example.com/me')), } - self.assertSetEqual(set(store.graph), instances) + self.assertSetEqual(set(store._graph), instances) # add some classes to the schema curr = curr + _schema.Schema.from_string(''' @@ -228,7 +228,7 @@ class TestSparqlStore(unittest.TestCase): store.schema = curr self.assertEqual(store.schema, curr) # instances have not changed - self.assertSetEqual(set(store.graph), instances) + self.assertSetEqual(set(store._graph), instances) # add some instances of the new classes p_partOf = curr.predicate(ns.bse.partOf) p_shared = curr.predicate(ns.bse.shared) @@ -247,7 +247,7 @@ class TestSparqlStore(unittest.TestCase): store.set(curr.node(ns.bsfs.Tag), {URI('http://example.com/me/tag#1234')}, p_principal, {URI('http://example.com/me/collection#1234')}) # new instances are now in the graph - self.assertSetEqual(set(store.graph), instances | { + self.assertSetEqual(set(store._graph), instances | { # collections (rdflib.URIRef('http://example.com/me/collection#1234'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Collection)), (rdflib.URIRef('http://example.com/me/collection#4321'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Collection)), @@ -315,7 +315,7 @@ class TestSparqlStore(unittest.TestCase): store.schema = curr self.assertEqual(store.schema, curr) # instances of old classes were removed - self.assertSetEqual(set(store.graph), { + self.assertSetEqual(set(store._graph), { # node instances (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Entity)), (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Entity)), @@ -409,14 +409,14 @@ class TestSparqlStore(unittest.TestCase): store.set(ent_type, ent_ids, p_tag, tag_ids) store.set(ent_type, ent_ids, p_filesize, {1234}) # current transaction is visible - self.assertSetEqual(set(store.graph), instances | { + self.assertSetEqual(set(store._graph), instances | { (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_filesize.uri), rdflib.Literal(1234, datatype=rdflib.XSD.integer)), (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_filesize.uri), rdflib.Literal(1234, datatype=rdflib.XSD.integer)), }) # rollback undoes previous changes store.rollback() - self.assertSetEqual(set(store.graph), set()) + self.assertSetEqual(set(store._graph), set()) # add some data once more store.create(ent_type, ent_ids) @@ -424,14 +424,14 @@ class TestSparqlStore(unittest.TestCase): store.set(ent_type, ent_ids, p_tag, tag_ids) store.set(ent_type, ent_ids, p_filesize, {1234}) # current transaction is visible - self.assertSetEqual(set(store.graph), instances | { + self.assertSetEqual(set(store._graph), instances | { (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_filesize.uri), rdflib.Literal(1234, datatype=rdflib.XSD.integer)), (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_filesize.uri), rdflib.Literal(1234, datatype=rdflib.XSD.integer)), }) # commit saves changes store.commit() - self.assertSetEqual(set(store.graph), instances | { + self.assertSetEqual(set(store._graph), instances | { (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_filesize.uri), rdflib.Literal(1234, datatype=rdflib.XSD.integer)), (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_filesize.uri), rdflib.Literal(1234, datatype=rdflib.XSD.integer)), }) @@ -440,7 +440,7 @@ class TestSparqlStore(unittest.TestCase): store.create(ent_type, {URI('http://example.com/me/entity#hello')}) store.set(ent_type, {URI('http://example.com/me/entity#hello')}, p_tag, tag_ids) store.set(ent_type, ent_ids, p_filesize, {4321}) - self.assertSetEqual(set(store.graph), instances | { + self.assertSetEqual(set(store._graph), instances | { (rdflib.URIRef('http://example.com/me/entity#hello'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Entity)), (rdflib.URIRef('http://example.com/me/entity#hello'), rdflib.URIRef(p_tag.uri), rdflib.URIRef('http://example.com/me/tag#1234')), (rdflib.URIRef('http://example.com/me/entity#hello'), rdflib.URIRef(p_tag.uri), rdflib.URIRef('http://example.com/me/tag#4321')), @@ -450,7 +450,7 @@ class TestSparqlStore(unittest.TestCase): # rollback undoes only changes since last commit store.rollback() - self.assertSetEqual(set(store.graph), instances | { + self.assertSetEqual(set(store._graph), instances | { (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_filesize.uri), rdflib.Literal(1234, datatype=rdflib.XSD.integer)), (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_filesize.uri), rdflib.Literal(1234, datatype=rdflib.XSD.integer)), }) @@ -507,14 +507,14 @@ class TestSparqlStore(unittest.TestCase): # can create some nodes ent_type = store.schema.node(ns.bsfs.Entity) store.create(ent_type, {URI('http://example.com/me/entity#1234'), URI('http://example.com/me/entity#4321')}) - self.assertSetEqual(set(store.graph), { + self.assertSetEqual(set(store._graph), { (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Entity)), (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Entity)), }) # existing nodes are skipped store.create(ent_type, {URI('http://example.com/me/entity#1234'), URI('http://example.com/me/entity#5678')}) - self.assertSetEqual(set(store.graph), { + self.assertSetEqual(set(store._graph), { # previous triples (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Entity)), (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Entity)), @@ -525,7 +525,7 @@ class TestSparqlStore(unittest.TestCase): # can create nodes of a different type tag_type = store.schema.node(ns.bsfs.Tag) store.create(tag_type, {URI('http://example.com/me/tag#1234'), URI('http://example.com/me/tag#4321')}) - self.assertSetEqual(set(store.graph), { + self.assertSetEqual(set(store._graph), { # previous triples (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Entity)), (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Entity)), @@ -538,7 +538,7 @@ class TestSparqlStore(unittest.TestCase): # creation does not change types of existing nodes tag_type = store.schema.node(ns.bsfs.Tag) store.create(tag_type, {URI('http://example.com/me/entity#1234'), URI('http://example.com/me/entity#4321')}) - self.assertSetEqual(set(store.graph), { + self.assertSetEqual(set(store._graph), { # previous triples (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Entity)), (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.RDF.type, rdflib.URIRef(ns.bsfs.Entity)), @@ -595,12 +595,12 @@ class TestSparqlStore(unittest.TestCase): self.assertRaises(errors.ConsistencyError, store.set, tag_type, tag_ids, p_filesize, {1234}) # empty value does not change the graph - plen = len(store.graph) + plen = len(store._graph) store.set(ent_type, ent_ids, p_filesize, []) store.set(ent_type, ent_ids, p_comment, []) store.set(ent_type, ent_ids, p_author, []) store.set(ent_type, ent_ids, p_tag, []) - self.assertEqual(plen, len(store.graph)) + self.assertEqual(plen, len(store._graph)) # cannot set multiple values on unique predicates self.assertRaises(ValueError, store.set, ent_type, ent_ids, p_filesize, {1234, 4321}) @@ -623,50 +623,50 @@ class TestSparqlStore(unittest.TestCase): store.set(ent_type, ent_ids, p_filesize, {1234}) self.assertIn( (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_filesize.uri), rdflib.Literal('1234', datatype=rdflib.XSD.integer)), - set(store.graph)) + set(store._graph)) self.assertIn( (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_filesize.uri), rdflib.Literal('1234', datatype=rdflib.XSD.integer)), - set(store.graph)) + set(store._graph)) # re-assigning the same node changes nothing store.set(ent_type, ent_ids, p_filesize, {1234}) self.assertIn( (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_filesize.uri), rdflib.Literal('1234', datatype=rdflib.XSD.integer)), - set(store.graph)) + set(store._graph)) self.assertIn( (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_filesize.uri), rdflib.Literal('1234', datatype=rdflib.XSD.integer)), - set(store.graph)) + set(store._graph)) # cannot set multiple unique literals self.assertRaises(ValueError, store.set, ent_type, ent_ids, p_filesize, {1234, 4321}) # same test as above # unique literals are overwritten by set store.set(ent_type, ent_ids, p_filesize, {4321}) self.assertIn( (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_filesize.uri), rdflib.Literal('4321', datatype=rdflib.XSD.integer)), - set(store.graph)) + set(store._graph)) self.assertNotIn( (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_filesize.uri), rdflib.Literal('1234', datatype=rdflib.XSD.integer)), - set(store.graph)) + set(store._graph)) self.assertIn( (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_filesize.uri), rdflib.Literal('4321', datatype=rdflib.XSD.integer)), - set(store.graph)) + set(store._graph)) self.assertNotIn( (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_filesize.uri), rdflib.Literal('1234', datatype=rdflib.XSD.integer)), - set(store.graph)) + set(store._graph)) # set non-unique literal store.set(ent_type, ent_ids, p_comment, {'foobar'}) - self.assertTrue(set(store.graph).issuperset({ + self.assertTrue(set(store._graph).issuperset({ (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_comment.uri), rdflib.Literal('foobar', datatype=rdflib.XSD.string)), (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_comment.uri), rdflib.Literal('foobar', datatype=rdflib.XSD.string)), })) # re-assigning the same node changes nothing store.set(ent_type, ent_ids, p_comment, {'foobar'}) - self.assertTrue(set(store.graph).issuperset({ + self.assertTrue(set(store._graph).issuperset({ (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_comment.uri), rdflib.Literal('foobar', datatype=rdflib.XSD.string)), (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_comment.uri), rdflib.Literal('foobar', datatype=rdflib.XSD.string)), })) # can set multiple non-unique literals at once store.set(ent_type, ent_ids, p_comment, {'foo', 'bar'}) - self.assertTrue(set(store.graph).issuperset({ + self.assertTrue(set(store._graph).issuperset({ (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_comment.uri), rdflib.Literal('foo', datatype=rdflib.XSD.string)), (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_comment.uri), rdflib.Literal('bar', datatype=rdflib.XSD.string)), (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_comment.uri), rdflib.Literal('foo', datatype=rdflib.XSD.string)), @@ -674,7 +674,7 @@ class TestSparqlStore(unittest.TestCase): })) # non-unique literals are appended by set store.set(ent_type, ent_ids, p_comment, {'hello world'}) - self.assertTrue(set(store.graph).issuperset({ + self.assertTrue(set(store._graph).issuperset({ (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_comment.uri), rdflib.Literal('foo', datatype=rdflib.XSD.string)), (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_comment.uri), rdflib.Literal('bar', datatype=rdflib.XSD.string)), (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_comment.uri), rdflib.Literal('hello world', datatype=rdflib.XSD.string)), @@ -687,50 +687,50 @@ class TestSparqlStore(unittest.TestCase): store.set(ent_type, ent_ids, p_author, {URI('http://example.com/me/user#1234')}) self.assertIn( (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_author.uri), rdflib.URIRef('http://example.com/me/user#1234')), - set(store.graph)) + set(store._graph)) self.assertIn( (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_author.uri), rdflib.URIRef('http://example.com/me/user#1234')), - set(store.graph)) + set(store._graph)) # re-assigning the same node changes nothing store.set(ent_type, ent_ids, p_author, {URI('http://example.com/me/user#1234')}) self.assertIn( (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_author.uri), rdflib.URIRef('http://example.com/me/user#1234')), - set(store.graph)) + set(store._graph)) self.assertIn( (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_author.uri), rdflib.URIRef('http://example.com/me/user#1234')), - set(store.graph)) + set(store._graph)) # cannot set multiple unique nodes self.assertRaises(ValueError, store.set, ent_type, ent_ids, p_author, {URI('http://example.com/me/user#1234'), URI('http://example.com/me/user#4321')}) # unique nodes are overwritten by set store.set(ent_type, ent_ids, p_author, {URI('http://example.com/me/user#4321')}) self.assertIn( (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_author.uri), rdflib.URIRef('http://example.com/me/user#4321')), - set(store.graph)) + set(store._graph)) self.assertNotIn( (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_author.uri), rdflib.URIRef('http://example.com/me/user#1234')), - set(store.graph)) + set(store._graph)) self.assertIn( (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_author.uri), rdflib.URIRef('http://example.com/me/user#4321')), - set(store.graph)) + set(store._graph)) self.assertNotIn( (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_author.uri), rdflib.URIRef('http://example.com/me/user#1234')), - set(store.graph)) + set(store._graph)) # set non-unique node store.set(ent_type, ent_ids, p_tag, {'http://example.com/me/tag#foobar'}) - self.assertTrue(set(store.graph).issuperset({ + self.assertTrue(set(store._graph).issuperset({ (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_tag.uri), rdflib.URIRef('http://example.com/me/tag#foobar')), (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_tag.uri), rdflib.URIRef('http://example.com/me/tag#foobar')), })) # re-assigning the same node changes nothing store.set(ent_type, ent_ids, p_tag, {'http://example.com/me/tag#foobar'}) - self.assertTrue(set(store.graph).issuperset({ + self.assertTrue(set(store._graph).issuperset({ (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_tag.uri), rdflib.URIRef('http://example.com/me/tag#foobar')), (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_tag.uri), rdflib.URIRef('http://example.com/me/tag#foobar')), })) # can set multiple non-unique literals at once store.set(ent_type, ent_ids, p_tag, {'http://example.com/me/tag#1234', 'http://example.com/me/tag#4321'}) - self.assertTrue(set(store.graph).issuperset({ + self.assertTrue(set(store._graph).issuperset({ (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_tag.uri), rdflib.URIRef('http://example.com/me/tag#1234')), (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_tag.uri), rdflib.URIRef('http://example.com/me/tag#4321')), (rdflib.URIRef('http://example.com/me/entity#4321'), rdflib.URIRef(p_tag.uri), rdflib.URIRef('http://example.com/me/tag#1234')), @@ -738,7 +738,7 @@ class TestSparqlStore(unittest.TestCase): })) # non-unique nodes are appended by set store.set(ent_type, ent_ids, p_tag, {'http://example.com/me/tag#foo', 'http://example.com/me/tag#bar'}) - self.assertTrue(set(store.graph).issuperset({ + self.assertTrue(set(store._graph).issuperset({ (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_tag.uri), rdflib.URIRef('http://example.com/me/tag#1234')), (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_tag.uri), rdflib.URIRef('http://example.com/me/tag#4321')), (rdflib.URIRef('http://example.com/me/entity#1234'), rdflib.URIRef(p_tag.uri), rdflib.URIRef('http://example.com/me/tag#foo')), @@ -750,10 +750,10 @@ class TestSparqlStore(unittest.TestCase): })) # nothing happens when no guids are given - plen = len(store.graph) + plen = len(store._graph) store.set(ent_type, set(), p_comment, {'xyz'}) store.set(ent_type, set(), p_tag, {URI('http://example.com/me/tag#xyz')}) - self.assertEqual(plen, len(store.graph)) + self.assertEqual(plen, len(store._graph)) # guids must be instances of node_type self.assertRaises(errors.InstanceError, store.set, ent_type, tag_ids, p_comment, {'xyz'}) -- cgit v1.2.3 From 3165c3609a5061135ff7393747f8dc3f7f7abe0c Mon Sep 17 00:00:00 2001 From: Matthias Baumgartner Date: Sun, 18 Dec 2022 14:07:56 +0100 Subject: graph schema migration --- test/graph/test_graph.py | 98 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) (limited to 'test') diff --git a/test/graph/test_graph.py b/test/graph/test_graph.py index eaeee0c..0b16527 100644 --- a/test/graph/test_graph.py +++ b/test/graph/test_graph.py @@ -84,6 +84,104 @@ class TestGraph(unittest.TestCase): # node_type must be in the schema self.assertRaises(KeyError, graph.nodes, ns.bsfs.Invalid, guids) + def test_migrate(self): + # setup + graph = Graph(self.backend, self.user) + + # argument must be a schema + class Foo(): pass + self.assertRaises(TypeError, graph.migrate, 'hello world') + self.assertRaises(TypeError, graph.migrate, 1234) + self.assertRaises(TypeError, graph.migrate, Foo()) + + # cannot append inconsistent schema + self.assertRaises(errors.ConsistencyError, graph.migrate, schema.Schema({}, { + schema.Node(ns.bsfs.Entity, + schema.Node(ns.bsfs.Intermediate, + schema.Node(ns.bsfs.Node, None)))}), append=True) + + # cannot migrate to inconsistent schema + self.assertRaises(errors.ConsistencyError, graph.migrate, schema.Schema({}, { + schema.Node(ns.bsfs.Entity, + schema.Node(ns.bsfs.Intermediate, + schema.Node(ns.bsfs.Node, None)))}), append=False) + + # can migrate to compatible schema + target_1 = schema.Schema.from_string(''' + prefix rdfs: + prefix xsd: + prefix bsfs: + prefix bse: + bsfs:Entity rdfs:subClassOf bsfs:Node . + xsd:string rdfs:subClassOf bsfs:Literal . + xsd:integer rdfs:subClassOf bsfs:Literal . + + bse:filename 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 . + + ''') + graph.migrate(target_1) + # new schema is applied + self.assertLess(target_1, graph.schema) + # graph appends its predicates + self.assertEqual(graph.schema, target_1 + schema.Schema.from_string(''' + prefix rdfs: + prefix xsd: + prefix bsfs: + prefix bsm: + xsd:integer rdfs:subClassOf bsfs:Literal . + bsm:t_created rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Node ; + rdfs:range xsd:integer ; + bsfs:unique "true"^^xsd:boolean . + ''')) + + # can overwrite the current schema + target_2 = schema.Schema.from_string(''' + prefix rdfs: + prefix xsd: + prefix bsfs: + prefix bse: + bsfs:Entity rdfs:subClassOf bsfs:Node . + xsd:string rdfs:subClassOf bsfs:Literal . + xsd:integer rdfs:subClassOf bsfs:Literal . + + bse:filename rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity ; + rdfs:range xsd:string ; + bsfs:unique "false"^^xsd:boolean . + + bse:author rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity ; + rdfs:range xsd:string ; + bsfs:unique "true"^^xsd:boolean . + + ''') + graph.migrate(target_2, append=False) + # append overwrites existing predicates + self.assertFalse(target_1 <= graph.schema) + # new schema is applied + self.assertLess(target_2, graph.schema) + # graph appends its predicates + self.assertEqual(graph.schema, target_2 + schema.Schema.from_string(''' + prefix rdfs: + prefix xsd: + prefix bsfs: + prefix bsm: + xsd:integer rdfs:subClassOf bsfs:Literal . + bsm:t_created rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Node ; + rdfs:range xsd:integer ; + bsfs:unique "true"^^xsd:boolean . + ''')) + ## main ## -- cgit v1.2.3 From 58496960926a56149c10d64e01b6df7d048eed0e Mon Sep 17 00:00:00 2001 From: Matthias Baumgartner Date: Sun, 18 Dec 2022 14:11:27 +0100 Subject: triple store Open interface --- test/graph/test_graph.py | 10 +++++----- test/graph/test_nodes.py | 4 ++-- test/triple_store/test_sparql.py | 20 ++++++++++---------- 3 files changed, 17 insertions(+), 17 deletions(-) (limited to 'test') diff --git a/test/graph/test_graph.py b/test/graph/test_graph.py index 0b16527..f8e107d 100644 --- a/test/graph/test_graph.py +++ b/test/graph/test_graph.py @@ -23,7 +23,7 @@ from bsfs.graph.graph import Graph class TestGraph(unittest.TestCase): def setUp(self): self.user = URI('http://example.com/me') - self.backend = SparqlStore.Open(None) + self.backend = SparqlStore.Open() self.backend.schema = schema.Schema.from_string(''' prefix rdfs: prefix bsfs: @@ -37,9 +37,9 @@ class TestGraph(unittest.TestCase): 'Graph(backend=SparqlStore(uri=None), user=http://example.com/me)') # str respects backend class Foo(SparqlStore): pass - self.assertEqual(str(Graph(Foo.Open(None), self.user)), + self.assertEqual(str(Graph(Foo.Open(), self.user)), 'Graph(Foo(uri=None), http://example.com/me)') - self.assertEqual(repr(Graph(Foo.Open(None), self.user)), + self.assertEqual(repr(Graph(Foo.Open(), self.user)), 'Graph(backend=Foo(uri=None), user=http://example.com/me)') # str respect user self.assertEqual(str(Graph(self.backend, URI('http://example.com/you'))), @@ -62,8 +62,8 @@ class TestGraph(unittest.TestCase): self.assertEqual(graph, Graph(self.backend, self.user)) self.assertEqual(hash(graph), hash(Graph(self.backend, self.user))) # equality respects backend - self.assertNotEqual(graph, Graph(SparqlStore.Open(None), self.user)) - self.assertNotEqual(hash(graph), hash(Graph(SparqlStore.Open(None), self.user))) + self.assertNotEqual(graph, Graph(SparqlStore.Open(), self.user)) + self.assertNotEqual(hash(graph), hash(Graph(SparqlStore.Open(), self.user))) # 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')))) diff --git a/test/graph/test_nodes.py b/test/graph/test_nodes.py index 71fbb10..43e7f6f 100644 --- a/test/graph/test_nodes.py +++ b/test/graph/test_nodes.py @@ -107,7 +107,7 @@ class TestNodes(unittest.TestCase): self.assertEqual(repr(nodes), f'Nodes({self.backend}, {self.user}, {self.ent_type}, {{\'http://example.com/me/entity#foo\'}})') # repr respects backend class Foo(SparqlStore): pass - backend = Foo.Open(None) + 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})') @@ -125,7 +125,7 @@ class TestNodes(unittest.TestCase): 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))) # equality respects backend - backend = SparqlStore.Open(None) + 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))) diff --git a/test/triple_store/test_sparql.py b/test/triple_store/test_sparql.py index ecf3a07..c4925c4 100644 --- a/test/triple_store/test_sparql.py +++ b/test/triple_store/test_sparql.py @@ -61,22 +61,22 @@ class TestSparqlStore(unittest.TestCase): ''') def test_essentials(self): - store = SparqlStore(None) + store = SparqlStore.Open() # equality self.assertEqual(store, store) self.assertEqual(hash(store), hash(store)) - self.assertNotEqual(store, SparqlStore(None)) - self.assertNotEqual(hash(store), hash(SparqlStore(None))) + self.assertNotEqual(store, SparqlStore.Open()) + self.assertNotEqual(hash(store), hash(SparqlStore.Open())) # string conversion self.assertEqual(str(store), 'SparqlStore(uri=None)') self.assertEqual(repr(store), 'SparqlStore(uri=None)') # open - self.assertIsInstance(SparqlStore.Open(None), SparqlStore) + self.assertIsInstance(SparqlStore.Open(), SparqlStore) def test__has_type(self): # setup store - store = SparqlStore(None) + store = SparqlStore.Open() store.schema = _schema.Schema.from_string(''' prefix rdfs: prefix xsd: @@ -129,7 +129,7 @@ class TestSparqlStore(unittest.TestCase): def test_schema(self): # setup - store = SparqlStore(None) + store = SparqlStore.Open() curr = self.schema p_comment = curr.predicate(ns.bse.comment) p_filesize = curr.predicate(ns.bse.filesize) @@ -380,7 +380,7 @@ class TestSparqlStore(unittest.TestCase): def test_transaction(self): # store setup - store = SparqlStore(None) + store = SparqlStore.Open() store.schema = self.schema p_tag = store.schema.predicate(ns.bse.tag) p_filesize = store.schema.predicate(ns.bse.filesize) @@ -458,7 +458,7 @@ class TestSparqlStore(unittest.TestCase): def test_exists(self): # store setup - store = SparqlStore(None) + store = SparqlStore.Open() store.schema = self.schema # prepare node types ent_type = store.schema.node(ns.bsfs.Entity) @@ -497,7 +497,7 @@ class TestSparqlStore(unittest.TestCase): def test_create(self): # setup - store = SparqlStore(None) + store = SparqlStore.Open() store.schema = self.schema # node type must be valid @@ -551,7 +551,7 @@ class TestSparqlStore(unittest.TestCase): def test_set(self): # store setup - store = SparqlStore(None) + store = SparqlStore.Open() store.schema = self.schema # prepare node types ent_type = store.schema.node(ns.bsfs.Entity) -- cgit v1.2.3 From a5ce14c8bbd55f4a078ceea9384cda56bf42a18b Mon Sep 17 00:00:00 2001 From: Matthias Baumgartner Date: Sun, 18 Dec 2022 14:16:06 +0100 Subject: SparqlStore.exists bugfix --- test/triple_store/test_sparql.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'test') diff --git a/test/triple_store/test_sparql.py b/test/triple_store/test_sparql.py index c4925c4..8d98749 100644 --- a/test/triple_store/test_sparql.py +++ b/test/triple_store/test_sparql.py @@ -476,23 +476,23 @@ class TestSparqlStore(unittest.TestCase): store.create(tag_type, tag_ids) # exists returns all existing nodes of the correct type - self.assertSetEqual(ent_ids, store.exists(ent_type, ent_ids)) - self.assertSetEqual(tag_ids, store.exists(tag_type, tag_ids)) + self.assertSetEqual(ent_ids, set(store.exists(ent_type, ent_ids))) + self.assertSetEqual(tag_ids, set(store.exists(tag_type, tag_ids))) # exists returns only nodes that match the type - self.assertSetEqual(set(), store.exists(ent_type, tag_ids)) - self.assertSetEqual({URI('http://example.com/me/entity#1234')}, store.exists(ent_type, { + self.assertSetEqual(set(), set(store.exists(ent_type, tag_ids))) + self.assertSetEqual({URI('http://example.com/me/entity#1234')}, set(store.exists(ent_type, { URI('http://example.com/me/tag#1234'), URI('http://example.com/me/entity#1234'), - })) + }))) # exists returns only nodes that exist - self.assertSetEqual(set(), store.exists(ent_type, { + self.assertSetEqual(set(), set(store.exists(ent_type, { URI('http://example.com/me/entity#foo'), URI('http://example.com/me/entity#bar'), - })) - self.assertSetEqual({URI('http://example.com/me/entity#1234')}, store.exists(ent_type, { + }))) + self.assertSetEqual({URI('http://example.com/me/entity#1234')}, set(store.exists(ent_type, { URI('http://example.com/me/entity#foo'), URI('http://example.com/me/entity#1234'), - })) + }))) def test_create(self): -- cgit v1.2.3 From 8ed8dbb4010a9a75cf6e61d185327825fe783776 Mon Sep 17 00:00:00 2001 From: Matthias Baumgartner Date: Sun, 18 Dec 2022 14:16:40 +0100 Subject: Graph.node interface --- test/graph/test_graph.py | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'test') diff --git a/test/graph/test_graph.py b/test/graph/test_graph.py index f8e107d..33cf6aa 100644 --- a/test/graph/test_graph.py +++ b/test/graph/test_graph.py @@ -74,6 +74,16 @@ class TestGraph(unittest.TestCase): self.assertEqual(graph.schema, self.backend.schema) self.assertRaises(AttributeError, setattr, graph, 'schema', None) + def test_node(self): + graph = Graph(self.backend, self.user) + 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})) + # 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) guids = {URI('http://example.com/me/entity#1234'), URI('http://example.com/me/entity#4321')} -- cgit v1.2.3 From 12d95ed8bda18f2ef9d36190919cb838bfb5efcf Mon Sep 17 00:00:00 2001 From: Matthias Baumgartner Date: Sun, 18 Dec 2022 14:17:44 +0100 Subject: bsfs lib and builders --- test/front/__init__.py | 0 test/front/test_bsfs.py | 38 +++++++++++++++++++++++++++ test/front/test_builder.py | 64 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+) create mode 100644 test/front/__init__.py create mode 100644 test/front/test_bsfs.py create mode 100644 test/front/test_builder.py (limited to 'test') diff --git a/test/front/__init__.py b/test/front/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/front/test_bsfs.py b/test/front/test_bsfs.py new file mode 100644 index 0000000..0d7f383 --- /dev/null +++ b/test/front/test_bsfs.py @@ -0,0 +1,38 @@ +""" + +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.graph import Graph +from bsfs.triple_store import SparqlStore +from bsfs.utils import errors, URI + +# objects to test +from bsfs.front.bsfs import Open + + +## code ## + +class TestBSFS(unittest.TestCase): + def test_open(self): + # valid config produces a valid graph + config = {'Graph': {'backend': {'SparqlStore': {}}, 'user': 'http://example.com/me'}} + graph = Open(config) + self.assertIsInstance(graph, Graph) + self.assertIsInstance(graph._backend, SparqlStore) + self.assertEqual(graph._user, URI('http://example.com/me')) + # invalid config raises an error + self.assertRaises(errors.ConfigError, Open, {}) + + +## main ## + +if __name__ == '__main__': + unittest.main() + +## EOF ## diff --git a/test/front/test_builder.py b/test/front/test_builder.py new file mode 100644 index 0000000..08f2027 --- /dev/null +++ b/test/front/test_builder.py @@ -0,0 +1,64 @@ +""" + +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.graph import Graph +from bsfs.triple_store import SparqlStore +from bsfs.utils import errors, URI + +# objects to test +from bsfs.front.builder import build_backend, build_graph + + +## code ## + +class TestBuilder(unittest.TestCase): + def test_build_backend(self): + # valid config produces a valid store + store = build_backend({'SparqlStore': {}}) + self.assertIsInstance(store, SparqlStore) + self.assertIsNone(store.uri) + # cannot create an invalid store + self.assertRaises(errors.ConfigError, build_backend, {'MyStore': {}}) + # must pass a dict + self.assertRaises(TypeError, build_backend, 1234) + self.assertRaises(TypeError, build_backend, 'hello world') + self.assertRaises(TypeError, build_backend, [1,2,3]) + # cannot create a store from an invalid config + self.assertRaises(errors.ConfigError, build_backend, {}) + self.assertRaises(errors.ConfigError, build_backend, {'SparqlStore': {}, 'OtherStore': {}}) + self.assertRaises(TypeError, build_backend, {'SparqlStore': {'hello': 'world'}}) + + def test_build_graph(self): + # valid config produces a valid graph + graph = build_graph({'Graph': {'backend': {'SparqlStore': {}}, 'user': 'http://example.com/me'}}) + self.assertIsInstance(graph, Graph) + self.assertIsInstance(graph._backend, SparqlStore) + self.assertEqual(graph._user, URI('http://example.com/me')) + # cannot create an invalid graph + self.assertRaises(errors.ConfigError, build_graph, {'MyGraph': {}}) + # must pass a dict + self.assertRaises(TypeError, build_graph, 1234) + self.assertRaises(TypeError, build_graph, 'hello world') + self.assertRaises(TypeError, build_graph, [1,2,3]) + # cannot create a graph from an invalid config + self.assertRaises(errors.ConfigError, build_graph, {}) + self.assertRaises(errors.ConfigError, build_graph, {'Graph': {}, 'Graph2': {}}) + self.assertRaises(errors.ConfigError, build_graph, {'Graph': {}}) + self.assertRaises(errors.ConfigError, build_graph, {'Graph': {'user': 'http://example.com/me'}}) + self.assertRaises(errors.ConfigError, build_graph, {'Graph': {'backend': 'Hello world'}}) + self.assertRaises(TypeError, build_graph, {'Graph': {'user': 'http://example.com/me', 'backend': 'Hello world'}}) + + +## main ## + +if __name__ == '__main__': + unittest.main() + +## EOF ## -- cgit v1.2.3 From e94368c75468e3e94382b12705e55d396249eaca Mon Sep 17 00:00:00 2001 From: Matthias Baumgartner Date: Sun, 18 Dec 2022 14:20:25 +0100 Subject: bsfs applications --- test/apps/__init__.py | 0 test/apps/config.json | 8 +++++ test/apps/schema-1.nt | 19 ++++++++++ test/apps/schema-2.nt | 19 ++++++++++ test/apps/test_init.py | 91 +++++++++++++++++++++++++++++++++++++++++++++++ test/apps/test_migrate.py | 66 ++++++++++++++++++++++++++++++++++ 6 files changed, 203 insertions(+) create mode 100644 test/apps/__init__.py create mode 100644 test/apps/config.json create mode 100644 test/apps/schema-1.nt create mode 100644 test/apps/schema-2.nt create mode 100644 test/apps/test_init.py create mode 100644 test/apps/test_migrate.py (limited to 'test') diff --git a/test/apps/__init__.py b/test/apps/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/apps/config.json b/test/apps/config.json new file mode 100644 index 0000000..ffc5ef7 --- /dev/null +++ b/test/apps/config.json @@ -0,0 +1,8 @@ +{ + "Graph": { + "user": "http://example.com/me", + "backend": { + "SparqlStore": {} + } + } +} diff --git a/test/apps/schema-1.nt b/test/apps/schema-1.nt new file mode 100644 index 0000000..e57146d --- /dev/null +++ b/test/apps/schema-1.nt @@ -0,0 +1,19 @@ + +prefix rdfs: +prefix xsd: + +# common bsfs prefixes +prefix bsfs: +prefix bse: + +# essential nodes +bsfs:Entity rdfs:subClassOf bsfs:Node . + +# common definitions +xsd:string rdfs:subClassOf bsfs:Literal . + +bse:filename rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity ; + rdfs:range xsd:string ; + bsfs:unique "false"^^xsd:boolean . + diff --git a/test/apps/schema-2.nt b/test/apps/schema-2.nt new file mode 100644 index 0000000..525ac99 --- /dev/null +++ b/test/apps/schema-2.nt @@ -0,0 +1,19 @@ + +prefix rdfs: +prefix xsd: + +# common bsfs prefixes +prefix bsfs: +prefix bse: + +# essential nodes +bsfs:Entity rdfs:subClassOf bsfs:Node . + +# common definitions +xsd:integer rdfs:subClassOf bsfs:Literal . + +bse:filesize rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity ; + rdfs:range xsd:integer ; + bsfs:unique "true"^^xsd:boolean . + diff --git a/test/apps/test_init.py b/test/apps/test_init.py new file mode 100644 index 0000000..bae6a68 --- /dev/null +++ b/test/apps/test_init.py @@ -0,0 +1,91 @@ +""" + +Part of the bsfs test suite. +A copy of the license is provided with the project. +Author: Matthias Baumgartner, 2022 +""" +# imports +import contextlib +import io +import json +import os +import tempfile +import unittest + +# bsie imports +from bsfs.front import build_graph +from bsfs.graph import Graph + +# objects to test +from bsfs.apps.init import main, init_sparql_store + + +## code ## + +class TestInit(unittest.TestCase): + def test_main(self): + + # cannot pass an invalid store + with contextlib.redirect_stderr(io.StringIO()): + self.assertRaises(SystemExit, main, ['--user', 'http://example.com/me', 'foobar']) + + # produces a config structure + outbuf = io.StringIO() + with contextlib.redirect_stdout(outbuf): + main(['--user', 'http://example.com/me', 'sparql']) + self.assertEqual(json.loads(outbuf.getvalue()), { + 'Graph': { + 'user': 'http://example.com/me', + 'backend': { + 'SparqlStore': {}}}}) + # config is valid + self.assertIsInstance(build_graph(json.loads(outbuf.getvalue())), Graph) + + # respects user flag + outbuf = io.StringIO() + with contextlib.redirect_stdout(outbuf): + main(['--user', 'http://example.com/you', 'sparql']) + self.assertEqual(json.loads(outbuf.getvalue()), { + 'Graph': { + 'user': 'http://example.com/you', + 'backend': { + 'SparqlStore': {}}}}) + + # respects output flag + _, path = tempfile.mkstemp(prefix='bsfs-test-', text=True) + outbuf = io.StringIO() + with contextlib.redirect_stdout(outbuf): + main(['--user', 'http://example.com/me', '--output', path, 'sparql']) + with open(path, 'rt') as ifile: + config = ifile.read() + os.unlink(path) + self.assertEqual(outbuf.getvalue(), '') + self.assertEqual(json.loads(config), { + 'Graph': { + 'user': 'http://example.com/me', + 'backend': { + 'SparqlStore': {}}}}) + + def test_init_sparql_store(self): + # returns a config structure + self.assertEqual(init_sparql_store('http://example.com/me'), { + 'Graph': { + 'user': 'http://example.com/me', + 'backend': { + 'SparqlStore': {}}}}) + # respects user + self.assertEqual(init_sparql_store('http://example.com/you'), { + 'Graph': { + 'user': 'http://example.com/you', + 'backend': { + 'SparqlStore': {}}}}) + # the config is valid + self.assertIsInstance(build_graph(init_sparql_store('http://example.com/me')), Graph) + + +## main ## + +if __name__ == '__main__': + unittest.main() + +## EOF ## diff --git a/test/apps/test_migrate.py b/test/apps/test_migrate.py new file mode 100644 index 0000000..957509a --- /dev/null +++ b/test/apps/test_migrate.py @@ -0,0 +1,66 @@ +""" + +Part of the bsfs test suite. +A copy of the license is provided with the project. +Author: Matthias Baumgartner, 2022 +""" +# imports +import contextlib +import io +import os +import sys +import unittest +import unittest.mock + +# bsie imports +from bsfs.schema import Schema + +# objects to test +from bsfs.apps.migrate import main + + +## code ## + +class TestMigrate(unittest.TestCase): + def test_main(self): + config = os.path.join(os.path.dirname(__file__), 'config.json') + schema_1 = os.path.join(os.path.dirname(__file__), 'schema-1.nt') + schema_2 = os.path.join(os.path.dirname(__file__), 'schema-2.nt') + + # provide no config + with contextlib.redirect_stderr(io.StringIO()): + self.assertRaises(SystemExit, main, []) + + # read schema from file + with open(schema_1) as ifile: + target = Schema.from_string(ifile.read()) + graph = main([config, schema_1]) + self.assertTrue(target <= graph.schema) + + # read schema from multiple files + with open(schema_1) as ifile: + target = Schema.from_string(ifile.read()) + with open(schema_2) as ifile: + target = target + Schema.from_string(ifile.read()) + graph = main([config, schema_1, schema_2]) + self.assertTrue(target <= graph.schema) + + # read schema from stdin + with open(schema_1, 'rt') as ifile: + target = Schema.from_string(ifile.read()) + with open(schema_1, 'rt') as ifile: + with unittest.mock.patch('sys.stdin', ifile): + graph = main([config]) + self.assertTrue(target <= graph.schema) + + # remove predicates + # NOTE: cannot currently test this since there's nothing to remove in the loaded (empty) schema. + + +## main ## + +if __name__ == '__main__': + unittest.main() + +## EOF ## + -- cgit v1.2.3