""" 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.Open() # equality self.assertEqual(store, store) self.assertEqual(hash(store), hash(store)) 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(), SparqlStore) def test__has_type(self): # setup store store = SparqlStore.Open() 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.Open() 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.Open() 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.Open() 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, 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(), 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(), 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')}, set(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.Open() 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.Open() 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 ##