def test_when_chunk_becomes_hidden_cached_data_is_cleared(self): """ When a ``Chunk`` becomes hidden, then its cached data is deleted. """ c = Chunk(data="<div/>", is_normal=True) c.save() keys = [c.display_key(kind) for kind in self.prepare_kinds] e = Entry() e.update(self.foo, "q", c, "foo", ChangeRecord.CREATE, ChangeRecord.MANUAL) db = ExistDB() self.assertIsNotNone(cache.get(c.display_key("xml"))) self.assertEqual(len(list_collection(db, self.chunk_collection_path)), 1) self.assertEqual( len(list_collection(db, self.display_collection_path)), 1) e.latest.hidden = True e.latest.save() for key in keys: self.assertIsNone(cache.get(key)) self.assertEqual(len(list_collection(db, self.chunk_collection_path)), 0) self.assertEqual( len(list_collection(db, self.display_collection_path)), 0)
def test_when_chunk_becomes_hidden_cached_data_is_cleared(self): """ When a ``Chunk`` becomes hidden, then its cached data is deleted. """ c = Chunk(data="<div/>", is_normal=True) c.save() keys = [c.display_key(kind) for kind in self.prepare_kinds] e = Entry() e.update( self.foo, "q", c, "foo", ChangeRecord.CREATE, ChangeRecord.MANUAL) db = ExistDB() self.assertIsNotNone(cache.get(c.display_key("xml"))) self.assertEqual(len(list_collection(db, self.chunk_collection_path)), 1) self.assertEqual(len(list_collection(db, self.display_collection_path)), 1) e.latest.hidden = True e.latest.save() for key in keys: self.assertIsNone(cache.get(key)) self.assertEqual(len(list_collection(db, self.chunk_collection_path)), 0) self.assertEqual(len(list_collection(db, self.display_collection_path)), 0)
def check_abnormal_remove_data_from_exist_and_cache(self, op): db = ExistDB() c = Chunk(data="<div/>", is_normal=False) c.clean() cache.delete(c.c_hash) method = getattr(c, op) db.removeCollection(self.chunk_collection_path, True) db.removeCollection(self.display_collection_path, True) c.save() keys = [c.display_key(kind) for kind in self.prepare_kinds] for kind in self.prepare_kinds: self.assertIsNone(cache.get(c.display_key(kind))) self.assertEqual(len(list_collection(db, self.chunk_collection_path)), 0) self.assertEqual(len(list_collection(db, self.display_collection_path)), 0) with mock.patch('lexicography.models.ExistDB.removeDocument') as \ remove_mock: method() self.assertEqual(remove_mock.call_count, 0) self.assertEqual(len(list_collection(db, self.chunk_collection_path)), 0) self.assertEqual(len(list_collection(db, self.display_collection_path)), 0) for key in keys: self.assertIsNone(cache.get(key))
def check_abnormal_remove_data_from_exist_and_cache(self, op): db = ExistDB() c = Chunk(data="<div/>", is_normal=False) c.clean() cache.delete(c.c_hash) method = getattr(c, op) db.removeCollection(self.chunk_collection_path, True) db.removeCollection(self.display_collection_path, True) c.save() keys = [c.display_key(kind) for kind in self.prepare_kinds] for kind in self.prepare_kinds: self.assertIsNone(cache.get(c.display_key(kind))) self.assertEqual(len(list_collection(db, self.chunk_collection_path)), 0) self.assertEqual( len(list_collection(db, self.display_collection_path)), 0) with mock.patch('lexicography.models.ExistDB.removeDocument') as \ remove_mock: method() self.assertEqual(remove_mock.call_count, 0) self.assertEqual(len(list_collection(db, self.chunk_collection_path)), 0) self.assertEqual( len(list_collection(db, self.display_collection_path)), 0) for key in keys: self.assertIsNone(cache.get(key))
def check_deletes_documents(self, op, collection, *args): c = Chunk(data="<div/>", is_normal=True) c.save() entry = self.make_reachable(c) # If it does not have metadata yet, that's fine. try: c.chunkmetadata.delete() except ChunkMetadata.DoesNotExist: pass # We have to delete the collection because merely saving the # chunk causes it to be synced, but this is not what we are # testing here. We want to make sure that calling # op will perform the sync. db = ExistDB() db.removeCollection(collection, True) self.assertEqual(len(list_collection(db, collection)), 0) op = getattr(self.manager, op) op(*args) self.assertEqual(len(list_collection(db, collection)), 1) # Make sure our chunk was not collected. self.assertEqual(self.manager.count(), 1) # Now we delete the chunk in SQL because we do not want the # ``delete`` method to be called, as it would take care of # removing the document itself. (And yes, we do interpolate # the table name. This is safe as ``Entry._meta.db_table`` is # a value under our control.) with connection.cursor() as cursor: cr = entry.latest cursor.execute( "DELETE FROM {} WHERE id = %s".format(entry._meta.db_table), [entry.pk]) # We have to do this ourselves because Django's cascading # delete is implemented at the ORM level, not the database # level. cursor.execute( "DELETE FROM {} WHERE id = %s".format(cr._meta.db_table), [cr.pk]) # Check that no collection or syncing has occurred. self.assertEqual(self.manager.count(), 1) self.assertEqual(len(list_collection(db, collection)), 1) op(*args) # Make sure our chunk was collected. self.assertEqual(self.manager.count(), 0) self.assertEqual(len(list_collection(db, collection)), 0)
def check_skip_abnormal_chunks(self, op, collection, *args): c = Chunk(data="", is_normal=False) c.save() self.make_reachable(c) db = ExistDB() self.assertEqual(len(list_collection(db, collection)), 0) getattr(self.manager, op)(*args) self.assertEqual(len(list_collection(db, collection)), 0) # Make sure our chunk was not collected. self.assertEqual(self.manager.count(), 1)
def check_skip_abnormal_chunks(self, op, collection, *args): c = Chunk(data="", is_normal=False) c.save() # We have to delete the collection because merely saving the # chunk causes it to be synced, but this is not what we are # testing here. We want to make sure that calling # sync_with_exist will perform the sync. db = ExistDB() db.removeCollection(collection, True) self.assertEqual(len(list_collection(db, collection)), 0) getattr(c, op)(*args) self.assertEqual(len(list_collection(db, collection)), 0)
def test_sync_handles_overwrites(self): """ ``sync_with_exist`` will not overwrite documents already in eXist. """ db = ExistDB() db.removeCollection(self.chunk_collection_path, True) c = Chunk(data="<div/>", is_normal=True) c.save() c.sync_with_exist() self.assertEqual(len(list_collection(db, self.chunk_collection_path)), 1) with mock.patch('lexicography.models.ExistDB.load') as load_mock: c.sync_with_exist() self.assertEqual(load_mock.call_count, 0, "load should not have been called!") self.assertEqual(len(list_collection(db, self.chunk_collection_path)), 1)
def check_syncs_normal_chunks(self, op, collection, *args): c = Chunk(data="<div/>", is_normal=True) c.save() self.make_reachable(c) # If it does not have metadata yet, that's fine. try: c.chunkmetadata.delete() except ChunkMetadata.DoesNotExist: pass # We have to delete the collection because merely saving the # chunk causes it to be synced, but this is not what we are # testing here. We want to make sure that calling # sync_with_exist will perform the sync. db = ExistDB() db.removeCollection(collection, True) self.assertEqual(len(list_collection(db, collection)), 0) getattr(self.manager, op)(*args) self.assertEqual(len(list_collection(db, collection)), 1) # Make sure our chunk was not collected. self.assertEqual(self.manager.count(), 1)
def check_remove_data_from_exist_and_cache(self, op): """ Check that invoking ``op`` will remove the data from the eXist database and the cache. """ db = ExistDB() c = Chunk(data="<div/>", is_normal=True) c.clean() method = op if callable(op) else getattr(c, op) cache.delete(c.c_hash) db.removeCollection(self.chunk_collection_path, True) db.removeCollection(self.display_collection_path, True) self.assertEqual(len(list_collection(db, self.chunk_collection_path)), 0) self.assertEqual(len(list_collection(db, self.display_collection_path)), 0) for kind in self.prepare_kinds: self.assertIsNone(cache.get(c.display_key(kind))) c.save() c._create_cached_data() keys = [c.display_key(kind) for kind in self.prepare_kinds] # Only the "xml" data is created on save. self.assertIsNotNone(cache.get(c.display_key("xml"))) self.assertEqual(len(list_collection(db, self.chunk_collection_path)), 1) self.assertEqual(len(list_collection(db, self.display_collection_path)), 1) method() self.assertEqual(len(list_collection(db, self.chunk_collection_path)), 0) self.assertEqual(len(list_collection(db, self.display_collection_path)), 0) for key in keys: self.assertIsNone(cache.get(key))
def check_remove_data_from_exist_and_cache(self, op): """ Check that invoking ``op`` will remove the data from the eXist database and the cache. """ db = ExistDB() c = Chunk(data="<div/>", is_normal=True) c.clean() method = op if isinstance(op, Callable) else getattr(c, op) cache.delete(c.c_hash) db.removeCollection(self.chunk_collection_path, True) db.removeCollection(self.display_collection_path, True) self.assertEqual(len(list_collection(db, self.chunk_collection_path)), 0) self.assertEqual( len(list_collection(db, self.display_collection_path)), 0) for kind in self.prepare_kinds: self.assertIsNone(cache.get(c.display_key(kind))) c.save() c._create_cached_data() keys = [c.display_key(kind) for kind in self.prepare_kinds] # Only the "xml" data is created on save. self.assertIsNotNone(cache.get(c.display_key("xml"))) self.assertEqual(len(list_collection(db, self.chunk_collection_path)), 1) self.assertEqual( len(list_collection(db, self.display_collection_path)), 1) method() self.assertEqual(len(list_collection(db, self.chunk_collection_path)), 0) self.assertEqual( len(list_collection(db, self.display_collection_path)), 0) for key in keys: self.assertIsNone(cache.get(key))
def _remove_absent(db, present, collection_path): for path in list_collection(db, collection_path): base = path.rsplit("/", 1)[-1] if base not in present: db.removeDocument(path, True)
def list_display_collection(self): db = ExistDB() return list_collection(db, self.chunk_collection_path)