diff options
Diffstat (limited to 'tests/units/document/volume.py')
-rwxr-xr-x | tests/units/document/volume.py | 971 |
1 files changed, 971 insertions, 0 deletions
diff --git a/tests/units/document/volume.py b/tests/units/document/volume.py new file mode 100755 index 0000000..40a0cae --- /dev/null +++ b/tests/units/document/volume.py @@ -0,0 +1,971 @@ +#!/usr/bin/env python +# sugar-lint: disable + +import os +import sys +import time +import shutil +import hashlib +from cStringIO import StringIO +from email.message import Message +from os.path import dirname, join, abspath, exists + +src_root = abspath(dirname(__file__)) + +from __init__ import tests + +import active_document as ad +from active_document import env, document, SingleVolume, \ + Request, Response, Document, active_property, \ + BlobProperty, NotFound +from active_document.volume import VolumeCommands +from active_toolkit import sockets, coroutine + + +class VolumeTest(tests.Test): + + def setUp(self): + tests.Test.setUp(self) + self.response = Response() + + def test_Populate(self): + self.touch( + ('document/1/1/guid', '{"value": "1"}'), + ('document/1/1/ctime', '{"value": 1}'), + ('document/1/1/mtime', '{"value": 1}'), + ('document/1/1/seqno', '{"value": 0}'), + + ('document/2/2/guid', '{"value": "2"}'), + ('document/2/2/ctime', '{"value": 2}'), + ('document/2/2/mtime', '{"value": 2}'), + ('document/2/2/seqno', '{"value": 0}'), + ) + + class Document(document.Document): + pass + + with SingleVolume(tests.tmpdir, [Document]) as volume: + for cls in volume.values(): + for __ in cls.populate(): + pass + self.assertEqual( + sorted(['1', '2']), + sorted([i.guid for i in volume['document'].find()[0]])) + + shutil.rmtree('document/index') + + class Document(document.Document): + pass + + with SingleVolume(tests.tmpdir, [Document]) as volume: + for cls in volume.values(): + for __ in cls.populate(): + pass + self.assertEqual( + sorted(['1', '2']), + sorted([i.guid for i in volume['document'].find()[0]])) + + def test_Commands(self): + + class TestDocument(Document): + + @active_property(slot=1, default='') + def prop(self, value): + return value + + @active_property(prefix='L', localized=True, default='') + def localized_prop(self, value): + return value + + self.volume = SingleVolume(tests.tmpdir, [TestDocument]) + self.volume['testdocument'].create(guid='guid') + + self.assertEqual({ + 'total': 1, + 'result': [ + {'guid': 'guid', 'prop': ''}, + ], + }, + self.call('GET', document='testdocument', reply=['guid', 'prop'])) + + guid_1 = self.call('POST', document='testdocument', content={'prop': 'value_1'}) + assert guid_1 + guid_2 = self.call('POST', document='testdocument', content={'prop': 'value_2'}) + assert guid_2 + + self.assertEqual( + sorted([ + {'guid': 'guid', 'prop': ''}, + {'guid': guid_1, 'prop': 'value_1'}, + {'guid': guid_2, 'prop': 'value_2'}, + ]), + sorted(self.call('GET', document='testdocument', reply=['guid', 'prop'])['result'])) + + self.call('PUT', document='testdocument', guid=guid_1, content={'prop': 'value_3'}) + + self.assertEqual( + sorted([ + {'guid': 'guid', 'prop': ''}, + {'guid': guid_1, 'prop': 'value_3'}, + {'guid': guid_2, 'prop': 'value_2'}, + ]), + sorted(self.call('GET', document='testdocument', reply=['guid', 'prop'])['result'])) + + self.call('DELETE', document='testdocument', guid=guid_2) + + self.assertEqual( + sorted([ + {'guid': 'guid', 'prop': ''}, + {'guid': guid_1, 'prop': 'value_3'}, + ]), + sorted(self.call('GET', document='testdocument', reply=['guid', 'prop'])['result'])) + + self.assertRaises(NotFound, self.call, 'GET', document='testdocument', guid=guid_2) + + self.assertEqual( + {'guid': guid_1, 'prop': 'value_3'}, + self.call('GET', document='testdocument', guid=guid_1, reply=['guid', 'prop'])) + + self.assertEqual( + 'value_3', + self.call('GET', document='testdocument', guid=guid_1, prop='prop')) + + def test_SetBLOBs(self): + + class TestDocument(Document): + + @active_property(BlobProperty) + def blob(self, value): + return value + + self.volume = SingleVolume(tests.tmpdir, [TestDocument]) + guid = self.call('POST', document='testdocument', content={}) + + self.assertRaises(RuntimeError, self.call, 'PUT', document='testdocument', guid=guid, prop='blob', content={'path': '/'}) + + self.call('PUT', document='testdocument', guid=guid, prop='blob', content='blob1') + self.assertEqual('blob1', file(self.call('GET', document='testdocument', guid=guid, prop='blob')['path']).read()) + + self.call('PUT', document='testdocument', guid=guid, prop='blob', content_stream=StringIO('blob2')) + self.assertEqual('blob2', file(self.call('GET', document='testdocument', guid=guid, prop='blob')['path']).read()) + + self.call('PUT', document='testdocument', guid=guid, prop='blob', content=None) + self.assertRaises(NotFound, self.call, 'GET', document='testdocument', guid=guid, prop='blob') + + def test_SetBLOBsWithMimeType(self): + + class TestDocument(Document): + + @active_property(BlobProperty, mime_type='default') + def blob(self, value): + return value + + self.volume = SingleVolume(tests.tmpdir, [TestDocument]) + guid = self.call('POST', document='testdocument', content={}) + + self.call('PUT', document='testdocument', guid=guid, prop='blob', content='blob1') + self.assertEqual('default', self.call('GET', document='testdocument', guid=guid, prop='blob')['mime_type']) + self.assertEqual('default', self.response.content_type) + + self.call('PUT', document='testdocument', guid=guid, prop='blob', content='blob1', content_type='foo') + self.assertEqual('foo', self.call('GET', document='testdocument', guid=guid, prop='blob')['mime_type']) + self.assertEqual('foo', self.response.content_type) + + def test_GetBLOBs(self): + + class TestDocument(Document): + + @active_property(BlobProperty) + def blob(self, value): + return value + + self.volume = SingleVolume(tests.tmpdir, [TestDocument]) + guid = self.call('POST', document='testdocument', content={}) + self.call('PUT', document='testdocument', guid=guid, prop='blob', content='blob') + + blob_path = tests.tmpdir + '/testdocument/%s/%s/blob' % (guid[:2], guid) + blob_meta = { + 'seqno': 2, + 'path': blob_path + '.blob', + 'digest': hashlib.sha1('blob').hexdigest(), + 'mime_type': 'application/octet-stream', + 'mtime': os.stat(blob_path).st_mtime, + } + + self.assertEqual('blob', file(self.call('GET', document='testdocument', guid=guid, prop='blob')['path']).read()) + + self.assertEqual( + {'guid': guid, 'blob': blob_meta}, + self.call('GET', document='testdocument', guid=guid, reply=['guid', 'blob'])) + + self.assertEqual([ + {'guid': guid, 'blob': blob_meta}, + ], + self.call('GET', document='testdocument', reply=['guid', 'blob'])['result']) + + def test_CommandsGetAbsentBlobs(self): + + class TestDocument(Document): + + @active_property(slot=1, default='') + def prop(self, value): + return value + + @active_property(BlobProperty) + def blob(self, value): + return value + + self.volume = SingleVolume(tests.tmpdir, [TestDocument]) + + guid = self.call('POST', document='testdocument', content={'prop': 'value'}) + self.assertEqual('value', self.call('GET', document='testdocument', guid=guid, prop='prop')) + self.assertRaises(NotFound, self.call, 'GET', document='testdocument', guid=guid, prop='blob') + self.assertEqual( + {'blob': ad.PropertyMeta()}, + self.call('GET', document='testdocument', guid=guid, reply=['blob'])) + + def test_Command_ReplyForGET(self): + + class TestDocument(Document): + + @active_property(slot=1, default='') + def prop(self, value): + return value + + @active_property(prefix='L', localized=True, default='') + def localized_prop(self, value): + return value + + self.volume = SingleVolume(tests.tmpdir, [TestDocument]) + guid = self.call('POST', document='testdocument', content={'prop': 'value'}) + + self.assertEqual( + ['guid', 'prop'], + self.call('GET', document='testdocument', guid=guid, reply=['guid', 'prop']).keys()) + + self.assertEqual( + ['guid'], + self.call('GET', document='testdocument')['result'][0].keys()) + + self.assertEqual( + sorted(['guid', 'prop']), + sorted(self.call('GET', document='testdocument', reply=['prop', 'guid'])['result'][0].keys())) + + self.assertEqual( + sorted(['prop']), + sorted(self.call('GET', document='testdocument', reply=['prop'])['result'][0].keys())) + + def test_DecodeBeforeSetting(self): + + class TestDocument(Document): + + @active_property(slot=1, typecast=int) + def prop(self, value): + return value + + self.volume = SingleVolume(tests.tmpdir, [TestDocument]) + + guid = self.call(method='POST', document='testdocument', content={'prop': '-1'}) + self.assertEqual(-1, self.call(method='GET', document='testdocument', guid=guid, prop='prop')) + + def test_LocalizedSet(self): + env.DEFAULT_LANG = 'en' + + class TestDocument(Document): + + @active_property(slot=1, default='') + def prop(self, value): + return value + + @active_property(prefix='L', localized=True, default='') + def localized_prop(self, value): + return value + + self.volume = SingleVolume(tests.tmpdir, [TestDocument]) + directory = self.volume['testdocument'] + + guid = directory.create({'localized_prop': 'value_raw'}) + self.assertEqual({'en': 'value_raw'}, directory.get(guid)['localized_prop']) + self.assertEqual( + [guid], + [i.guid for i in directory.find(0, 100, localized_prop='value_raw')[0]]) + + directory.update(guid, {'localized_prop': 'value_raw2'}) + self.assertEqual({'en': 'value_raw2'}, directory.get(guid)['localized_prop']) + self.assertEqual( + [guid], + [i.guid for i in directory.find(0, 100, localized_prop='value_raw2')[0]]) + + guid = self.call('POST', document='testdocument', accept_language=['ru'], content={'localized_prop': 'value_ru'}) + self.assertEqual({'ru': 'value_ru'}, directory.get(guid)['localized_prop']) + self.assertEqual( + [guid], + [i.guid for i in directory.find(0, 100, localized_prop='value_ru')[0]]) + + self.call('PUT', document='testdocument', guid=guid, accept_language=['en'], content={'localized_prop': 'value_en'}) + self.assertEqual({'ru': 'value_ru', 'en': 'value_en'}, directory.get(guid)['localized_prop']) + self.assertEqual( + [guid], + [i.guid for i in directory.find(0, 100, localized_prop='value_ru')[0]]) + self.assertEqual( + [guid], + [i.guid for i in directory.find(0, 100, localized_prop='value_en')[0]]) + + def test_LocalizedGet(self): + + class TestDocument(Document): + + @active_property(slot=1, default='') + def prop(self, value): + return value + + @active_property(prefix='L', localized=True, default='') + def localized_prop(self, value): + return value + + self.volume = SingleVolume(tests.tmpdir, [TestDocument]) + directory = self.volume['testdocument'] + + guid = self.call('POST', document='testdocument', content={ + 'localized_prop': { + 'ru': 'value_ru', + 'es': 'value_es', + 'en': 'value_en', + }, + }) + + env.DEFAULT_LANG = 'en' + + self.assertEqual( + {'localized_prop': 'value_en'}, + self.call('GET', document='testdocument', guid=guid, reply=['localized_prop'])) + self.assertEqual( + {'localized_prop': 'value_ru'}, + self.call('GET', document='testdocument', guid=guid, accept_language=['ru'], reply=['localized_prop'])) + self.assertEqual( + 'value_ru', + self.call('GET', document='testdocument', guid=guid, accept_language=['ru', 'es'], prop='localized_prop')) + self.assertEqual( + [{'localized_prop': 'value_ru'}], + self.call('GET', document='testdocument', accept_language=['foo', 'ru', 'es'], reply=['localized_prop'])['result']) + + self.assertEqual( + {'localized_prop': 'value_ru'}, + self.call('GET', document='testdocument', guid=guid, accept_language=['ru-RU'], reply=['localized_prop'])) + self.assertEqual( + 'value_ru', + self.call('GET', document='testdocument', guid=guid, accept_language=['ru-RU', 'es'], prop='localized_prop')) + self.assertEqual( + [{'localized_prop': 'value_ru'}], + self.call('GET', document='testdocument', accept_language=['foo', 'ru-RU', 'es'], reply=['localized_prop'])['result']) + + self.assertEqual( + {'localized_prop': 'value_es'}, + self.call('GET', document='testdocument', guid=guid, accept_language=['es'], reply=['localized_prop'])) + self.assertEqual( + 'value_es', + self.call('GET', document='testdocument', guid=guid, accept_language=['es', 'ru'], prop='localized_prop')) + self.assertEqual( + [{'localized_prop': 'value_es'}], + self.call('GET', document='testdocument', accept_language=['foo', 'es', 'ru'], reply=['localized_prop'])['result']) + + self.assertEqual( + {'localized_prop': 'value_en'}, + self.call('GET', document='testdocument', guid=guid, accept_language=['fr'], reply=['localized_prop'])) + self.assertEqual( + 'value_en', + self.call('GET', document='testdocument', guid=guid, accept_language=['fr', 'za'], prop='localized_prop')) + self.assertEqual( + [{'localized_prop': 'value_en'}], + self.call('GET', document='testdocument', accept_language=['foo', 'fr', 'za'], reply=['localized_prop'])['result']) + + env.DEFAULT_LANG = 'foo' + fallback_lang = sorted(['ru', 'es', 'en'])[0] + + self.assertEqual( + {'localized_prop': 'value_%s' % fallback_lang}, + self.call('GET', document='testdocument', guid=guid, accept_language=['fr'], reply=['localized_prop'])) + self.assertEqual( + 'value_%s' % fallback_lang, + self.call('GET', document='testdocument', guid=guid, accept_language=['fr', 'za'], prop='localized_prop')) + self.assertEqual( + [{'localized_prop': 'value_%s' % fallback_lang}], + self.call('GET', document='testdocument', accept_language=['foo', 'fr', 'za'], reply=['localized_prop'])['result']) + + def test_LazyOpen(self): + + class Document1(document.Document): + pass + + class Document2(document.Document): + pass + + volume = SingleVolume('.', [Document1, Document2], lazy_open=True) + assert not exists('document1/index') + assert not exists('document2/index') + volume['document1'].find() + volume['document2'].find() + assert exists('document1/index') + assert exists('document2/index') + volume['document1'].find() + volume['document2'].find() + volume.close() + + shutil.rmtree('document1') + shutil.rmtree('document2') + + volume = SingleVolume('.', [Document1, Document2], lazy_open=False) + assert exists('document1/index') + assert exists('document2/index') + volume.close() + + def test_OpenByModuleName(self): + self.touch( + ('foo/bar.py', [ + 'from active_document import Document', + 'class Bar(Document): pass', + ]), + ('foo/__init__.py', ''), + ) + sys.path.insert(0, '.') + + volume = SingleVolume('.', ['foo.bar']) + assert exists('bar/index') + volume['bar'].find() + volume.close() + + def test_Command_GetBlobSetByUrl(self): + + class TestDocument(Document): + + @active_property(slot=1, default='') + def prop(self, value): + return value + + @active_property(BlobProperty) + def blob(self, value): + return value + + @active_property(prefix='L', localized=True, default='') + def localized_prop(self, value): + return value + + self.volume = SingleVolume(tests.tmpdir, [TestDocument]) + guid = self.call('POST', document='testdocument', content={}) + self.call('PUT', document='testdocument', guid=guid, prop='blob', url='http://sugarlabs.org') + + self.assertEqual( + 'http://sugarlabs.org', + self.call('GET', document='testdocument', guid=guid, prop='blob')['url']) + + def test_before_create(self): + + class TestDocument(Document): + + @active_property(slot=1, default='') + def prop(self, value): + return value + + @active_property(prefix='L', localized=True, default='') + def localized_prop(self, value): + return value + + self.volume = SingleVolume(tests.tmpdir, [TestDocument]) + + ts = time.time() + guid = self.call(method='POST', document='testdocument', content={}) + assert self.volume['testdocument'].get(guid)['ctime'] in range(ts - 1, ts + 1) + assert self.volume['testdocument'].get(guid)['mtime'] in range(ts - 1, ts + 1) + + def test_before_create_Override(self): + + class Commands(VolumeCommands): + + def before_create(self, request, props): + props['prop'] = 'overriden' + VolumeCommands.before_create(self, request, props) + + class TestDocument(Document): + + @active_property(slot=1, default='') + def prop(self, value): + return value + + @active_property(prefix='L', localized=True, default='') + def localized_prop(self, value): + return value + + volume = SingleVolume(tests.tmpdir, [TestDocument]) + cp = Commands(volume) + + request = Request(method='POST', document='testdocument') + request.content = {'prop': 'foo'} + guid = cp.call(request, Response()) + self.assertEqual('overriden', volume['testdocument'].get(guid)['prop']) + + request = Request(method='PUT', document='testdocument', guid=guid) + request.content = {'prop': 'bar'} + cp.call(request, Response()) + self.assertEqual('bar', volume['testdocument'].get(guid)['prop']) + + def test_before_update(self): + + class TestDocument(Document): + + @active_property(slot=1, default='') + def prop(self, value): + return value + + @active_property(prefix='L', localized=True, default='') + def localized_prop(self, value): + return value + + self.volume = SingleVolume(tests.tmpdir, [TestDocument]) + guid = self.call(method='POST', document='testdocument', content={}) + prev_mtime = self.volume['testdocument'].get(guid)['mtime'] + + time.sleep(1) + + self.call(method='PUT', document='testdocument', guid=guid, content={'prop': 'probe'}) + assert self.volume['testdocument'].get(guid)['mtime'] - prev_mtime >= 1 + + def test_before_update_Override(self): + + class Commands(VolumeCommands): + + def before_update(self, request, props): + props['prop'] = 'overriden' + VolumeCommands.before_update(self, request, props) + + class TestDocument(Document): + + @active_property(slot=1, default='') + def prop(self, value): + return value + + @active_property(prefix='L', localized=True, default='') + def localized_prop(self, value): + return value + + volume = SingleVolume(tests.tmpdir, [TestDocument]) + cp = Commands(volume) + + request = Request(method='POST', document='testdocument') + request.content = {'prop': 'foo'} + guid = cp.call(request, Response()) + self.assertEqual('foo', volume['testdocument'].get(guid)['prop']) + + request = Request(method='PUT', document='testdocument', guid=guid) + request.content = {'prop': 'bar'} + cp.call(request, Response()) + self.assertEqual('overriden', volume['testdocument'].get(guid)['prop']) + + def test_DoNotPassGuidsForCreate(self): + + class TestDocument(Document): + + @active_property(slot=1, default='') + def prop(self, value): + return value + + @active_property(prefix='L', localized=True, default='') + def localized_prop(self, value): + return value + + self.volume = SingleVolume(tests.tmpdir, [TestDocument]) + self.assertRaises(env.Forbidden, self.call, method='POST', document='testdocument', content={'guid': 'foo'}) + guid = self.call(method='POST', document='testdocument', content={}) + assert guid + + def test_seqno(self): + + class Document1(Document): + pass + + class Document2(Document): + pass + + volume = SingleVolume(tests.tmpdir, [Document1, Document2]) + + assert not exists('seqno') + self.assertEqual(0, volume.seqno.value) + + volume['document1'].create(guid='1') + self.assertEqual(1, volume['document1'].get('1')['seqno']) + volume['document2'].create(guid='1') + self.assertEqual(2, volume['document2'].get('1')['seqno']) + volume['document1'].create(guid='2') + self.assertEqual(3, volume['document1'].get('2')['seqno']) + volume['document2'].create(guid='2') + self.assertEqual(4, volume['document2'].get('2')['seqno']) + + self.assertEqual(4, volume.seqno.value) + assert not exists('seqno') + volume.seqno.commit() + assert exists('seqno') + volume = SingleVolume(tests.tmpdir, [Document1, Document2]) + self.assertEqual(4, volume.seqno.value) + + def test_Events(self): + env.index_flush_threshold.value = 0 + env.index_flush_timeout.value = 0 + + class Document1(Document): + + @active_property(slot=1, default='') + def prop(self, value): + pass + + class Document2(Document): + + @active_property(slot=1, default='') + def prop(self, value): + pass + + @active_property(BlobProperty) + def blob(self, value): + return value + + self.touch( + ('document1/1/1/guid', '{"value": "1"}'), + ('document1/1/1/ctime', '{"value": 1}'), + ('document1/1/1/mtime', '{"value": 1}'), + ('document1/1/1/prop', '{"value": ""}'), + ('document1/1/1/seqno', '{"value": 0}'), + ) + + events = [] + volume = SingleVolume(tests.tmpdir, [Document1, Document2]) + volume.connect(lambda event: events.append(event)) + + volume.populate() + self.assertEqual([ + {'event': 'commit', 'document': 'document1'}, + {'event': 'populate', 'document': 'document1'}, + ], + events) + del events[:] + + volume['document1'].create(guid='guid1') + volume['document2'].create(guid='guid2') + self.assertEqual([ + {'event': 'create', 'document': 'document1', 'guid': 'guid1', 'props': { + 'ctime': 0, + 'mtime': 0, + 'seqno': 0, + 'prop': '', + 'guid': 'guid1', + }}, + {'event': 'create', 'document': 'document2', 'guid': 'guid2', 'props': { + 'ctime': 0, + 'mtime': 0, + 'seqno': 0, + 'prop': '', + 'guid': 'guid2', + }}, + ], + events) + del events[:] + + volume['document1'].update('guid1', prop='foo') + volume['document2'].update('guid2', prop='bar') + self.assertEqual([ + {'event': 'update', 'document': 'document1', 'guid': 'guid1', 'props': { + 'prop': 'foo', + }}, + {'event': 'update', 'document': 'document2', 'guid': 'guid2', 'props': { + 'prop': 'bar', + }}, + ], + events) + del events[:] + + volume['document2'].set_blob('guid2', 'blob', StringIO('blob')) + self.assertEqual([ + {'event': 'update', 'document': 'document2', 'guid': 'guid2', 'props': { + 'seqno': 5, + }}, + ], + events) + del events[:] + + volume['document1'].delete('guid1') + self.assertEqual([ + {'event': 'delete', 'document': 'document1', 'guid': 'guid1'}, + ], + events) + del events[:] + + volume['document1'].commit() + volume['document2'].commit() + + self.assertEqual([ + {'event': 'commit', 'document': 'document1'}, + {'event': 'commit', 'document': 'document2'}, + ], + events) + + def test_PermissionsNoWrite(self): + + class TestDocument(Document): + + @active_property(slot=1, default='', permissions=ad.ACCESS_READ) + def prop(self, value): + pass + + @active_property(BlobProperty, permissions=ad.ACCESS_READ) + def blob(self, value): + return value + + self.volume = SingleVolume(tests.tmpdir, [TestDocument]) + guid = self.call('POST', document='testdocument', content={}) + + self.assertRaises(ad.Forbidden, self.call, 'POST', document='testdocument', content={'prop': 'value'}) + self.assertRaises(ad.Forbidden, self.call, 'PUT', document='testdocument', guid=guid, content={'prop': 'value'}) + self.assertRaises(ad.Forbidden, self.call, 'PUT', document='testdocument', guid=guid, content={'blob': 'value'}) + self.assertRaises(ad.Forbidden, self.call, 'PUT', document='testdocument', guid=guid, prop='prop', content='value') + self.assertRaises(ad.Forbidden, self.call, 'PUT', document='testdocument', guid=guid, prop='blob', content='value') + + def test_BlobsWritePermissions(self): + + class TestDocument(Document): + + @active_property(BlobProperty, permissions=ad.ACCESS_CREATE | ad.ACCESS_WRITE) + def blob1(self, value): + return value + + @active_property(BlobProperty, permissions=ad.ACCESS_CREATE) + def blob2(self, value): + return value + + self.volume = SingleVolume(tests.tmpdir, [TestDocument]) + + guid = self.call('POST', document='testdocument', content={}) + self.call('PUT', document='testdocument', guid=guid, content={'blob1': 'value1', 'blob2': 'value2'}) + self.call('PUT', document='testdocument', guid=guid, content={'blob1': 'value1'}) + self.assertRaises(ad.Forbidden, self.call, 'PUT', document='testdocument', guid=guid, content={'blob2': 'value2_'}) + + guid = self.call('POST', document='testdocument', content={}) + self.call('PUT', document='testdocument', guid=guid, prop='blob1', content='value1') + self.call('PUT', document='testdocument', guid=guid, prop='blob2', content='value2') + self.call('PUT', document='testdocument', guid=guid, prop='blob1', content='value1_') + self.assertRaises(ad.Forbidden, self.call, 'PUT', document='testdocument', guid=guid, prop='blob2', content='value2_') + + def test_properties_OverrideGet(self): + + class TestDocument(Document): + + @active_property(slot=1, default='1') + def prop1(self, value): + return value + + @active_property(slot=2, default='2') + def prop2(self, value): + return -1 + + @active_property(BlobProperty) + def blob(self, meta): + meta['path'] = 'new-blob' + return meta + + self.volume = SingleVolume(tests.tmpdir, [TestDocument]) + guid = self.call('POST', document='testdocument', content={}) + self.touch(('new-blob', 'new-blob')) + self.call('PUT', document='testdocument', guid=guid, prop='blob', content='old-blob') + + self.assertEqual( + 'new-blob', + self.call('GET', document='testdocument', guid=guid, prop='blob')['path']) + self.assertEqual( + '1', + self.call('GET', document='testdocument', guid=guid, prop='prop1')) + self.assertEqual( + -1, + self.call('GET', document='testdocument', guid=guid, prop='prop2')) + self.assertEqual( + {'prop1': '1', 'prop2': -1}, + self.call('GET', document='testdocument', guid=guid, reply=['prop1', 'prop2'])) + + def test_properties_OverrideSet(self): + + class TestDocument(Document): + + @active_property(slot=1, default='1') + def prop(self, value): + return value + + @prop.setter + def prop(self, value): + return '_%s' % value + + @active_property(BlobProperty) + def blob1(self, meta): + return meta + + @blob1.setter + def blob1(self, value): + return ad.PropertyMeta(url=value) + + @active_property(BlobProperty) + def blob2(self, meta): + return meta + + @blob2.setter + def blob2(self, value): + return ' %s ' % value + + self.volume = SingleVolume(tests.tmpdir, [TestDocument]) + guid = self.call('POST', document='testdocument', content={}) + + self.assertEqual('1', self.call('GET', document='testdocument', guid=guid, prop='prop')) + self.assertRaises(NotFound, self.call, 'GET', document='testdocument', guid=guid, prop='blob1') + + self.call('PUT', document='testdocument', guid=guid, prop='prop', content='2') + self.assertEqual('_2', self.call('GET', document='testdocument', guid=guid, prop='prop')) + self.assertRaises(NotFound, self.call, 'GET', document='testdocument', guid=guid, prop='blob1') + + self.call('PUT', document='testdocument', guid=guid, content={'prop': 3}) + self.assertEqual('_3', self.call('GET', document='testdocument', guid=guid, prop='prop')) + self.assertRaises(NotFound, self.call, 'GET', document='testdocument', guid=guid, prop='blob1') + + self.call('PUT', document='testdocument', guid=guid, prop='blob1', content='blob2') + self.assertEqual('blob2', self.call('GET', document='testdocument', guid=guid, prop='blob1')['url']) + + guid = self.call('POST', document='testdocument', content={'blob2': 'foo'}) + self.assertEqual(' foo ', file(self.call('GET', document='testdocument', guid=guid, prop='blob2')['path']).read()) + + self.call('PUT', document='testdocument', guid=guid, prop='blob2', content='bar') + self.assertEqual(' bar ', file(self.call('GET', document='testdocument', guid=guid, prop='blob2')['path']).read()) + + def test_SubCall(self): + + class TestDocument(Document): + + @active_property(BlobProperty, mime_type='application/json') + def blob(self, value): + return value + + @blob.setter + def blob(self, value): + if '!' not in value: + meta = self.meta('blob') + if meta: + value = file(meta['path']).read() + value + coroutine.spawn(self.post, value) + return value + + def post(self, value): + self.request.call('PUT', document='testdocument', guid=self.guid, prop='blob', content=value + '!') + + self.volume = SingleVolume(tests.tmpdir, [TestDocument]) + + guid = self.call('POST', document='testdocument', content={'blob': '0'}) + coroutine.dispatch() + self.assertEqual('0!', file(self.call('GET', document='testdocument', guid=guid, prop='blob')['path']).read()) + + self.call('PUT', document='testdocument', guid=guid, prop='blob', content='1') + coroutine.dispatch() + self.assertEqual('0!1!', file(self.call('GET', document='testdocument', guid=guid, prop='blob')['path']).read()) + + def test_Group(self): + + class TestDocument(Document): + + @active_property(slot=1) + def prop(self, value): + return value + + self.volume = SingleVolume(tests.tmpdir, [TestDocument]) + + self.call('POST', document='testdocument', content={'prop': 1}) + self.call('POST', document='testdocument', content={'prop': 2}) + self.call('POST', document='testdocument', content={'prop': 1}) + + self.assertEqual( + sorted([{'prop': 1}, {'prop': 2}]), + sorted(self.call('GET', document='testdocument', reply='prop', group_by='prop')['result'])) + + def test_CallSetterEvenIfThereIsNoCreatePermissions(self): + + class TestDocument(Document): + + @active_property(slot=1, permissions=ad.ACCESS_READ, default=0) + def prop(self, value): + return value + + @prop.setter + def prop(self, value): + return value + 1 + + self.volume = SingleVolume(tests.tmpdir, [TestDocument]) + + self.assertRaises(ad.Forbidden, self.call, 'POST', document='testdocument', content={'prop': 1}) + + guid = self.call('POST', document='testdocument', content={}) + self.assertEqual(1, self.call('GET', document='testdocument', guid=guid, prop='prop')) + + def test_ReturnDefualtsForMissedProps(self): + + class TestDocument(Document): + + @active_property(slot=1, default='default') + def prop(self, value): + return value + + self.volume = SingleVolume(tests.tmpdir, [TestDocument]) + guid = self.call('POST', document='testdocument', content={'prop': 'set'}) + + self.assertEqual( + [{'prop': 'set'}], + self.call('GET', document='testdocument', reply='prop')['result']) + self.assertEqual( + {'prop': 'set'}, + self.call('GET', document='testdocument', guid=guid, reply='prop')) + self.assertEqual( + 'set', + self.call('GET', document='testdocument', guid=guid, prop='prop')) + + os.unlink('testdocument/%s/%s/prop' % (guid[:2], guid)) + + self.assertEqual( + [{'prop': 'default'}], + self.call('GET', document='testdocument', reply='prop')['result']) + self.assertEqual( + {'prop': 'default'}, + self.call('GET', document='testdocument', guid=guid, reply='prop')) + self.assertEqual( + 'default', + self.call('GET', document='testdocument', guid=guid, prop='prop')) + + def call(self, method, document=None, guid=None, prop=None, + accept_language=None, content=None, content_stream=None, + content_type=None, if_modified_since=None, **kwargs): + + class TestRequest(Request): + + content_stream = None + content_length = 0 + + request = TestRequest(**kwargs) + request.content = content + request.content_stream = content_stream + request.content_type = content_type + request.accept_language = accept_language + request.if_modified_since = if_modified_since + request['method'] = method + if document: + request['document'] = document + if guid: + request['guid'] = guid + if prop: + request['prop'] = prop + if request.content_stream is not None: + request.content_length = len(request.content_stream.getvalue()) + + self.response = Response() + cp = VolumeCommands(self.volume) + return cp.call(request, self.response) + + +if __name__ == '__main__': + tests.main() |