diff options
Diffstat (limited to 'tests/units')
28 files changed, 3486 insertions, 3706 deletions
diff --git a/tests/units/client/cache.py b/tests/units/client/cache.py index e980549..51245ee 100755 --- a/tests/units/client/cache.py +++ b/tests/units/client/cache.py @@ -12,7 +12,6 @@ from __init__ import tests from sugar_network import db from sugar_network.model.context import Context -from sugar_network.model.release import Release from sugar_network.client import cache_limit, cache_limit_percent, cache_lifetime, IPCConnection from sugar_network.client.cache import Cache from sugar_network.toolkit import http @@ -32,7 +31,7 @@ class CacheTest(tests.Test): self.override(os, 'statvfs', lambda *args: statvfs()) def test_open(self): - volume = db.Volume('db', [Context, Release]) + volume = db.Volume('db', [Context]) volume['release'].create({ 'guid': '1', @@ -83,7 +82,7 @@ class CacheTest(tests.Test): self.assertEqual(['5', '4', '1'], [i for i in cache]) def test_open_IgnoreClones(self): - volume = db.Volume('db', [Context, Release]) + volume = db.Volume('db', [Context]) volume['context'].create({ 'guid': 'context', @@ -109,7 +108,7 @@ class CacheTest(tests.Test): self.assertEqual([], [i for i in cache]) def test_ensure_AfterOpen(self): - volume = db.Volume('db', [Context, Release]) + volume = db.Volume('db', [Context]) volume['release'].create({'data': {'blob_size': 1}, 'guid': '1', 'context': 'context', 'version': '1', 'license': ['GPL'], 'stability': 'stable'}) os.utime('db/release/1/1', (1, 1)) @@ -143,7 +142,7 @@ class CacheTest(tests.Test): self.assertRaises(RuntimeError, cache.ensure, 2, 0) def test_ensure_Live(self): - volume = db.Volume('db', [Context, Release]) + volume = db.Volume('db', [Context]) cache = Cache(volume) # To initiate the cache @@ -159,7 +158,7 @@ class CacheTest(tests.Test): self.assertRaises(RuntimeError, cache.ensure, 1, 0) def test_ensure_ConsiderTmpSize(self): - volume = db.Volume('db', [Context, Release]) + volume = db.Volume('db', [Context]) volume['release'].create({'data': {'blob_size': 1}, 'guid': '1', 'context': 'context', 'version': '1', 'license': ['GPL'], 'stability': 'stable'}) cache = Cache(volume) @@ -175,7 +174,7 @@ class CacheTest(tests.Test): def test_recycle(self): ts = time.time() - volume = db.Volume('db', [Context, Release]) + volume = db.Volume('db', [Context]) volume['release'].create({'data': {'blob_size': 1}, 'guid': '1', 'context': 'context', 'version': '1', 'license': ['GPL'], 'stability': 'stable'}) os.utime('db/release/1/1', (ts - 1.5 * 86400, ts - 1.5 * 86400)) volume['release'].create({'data': {'blob_size': 1}, 'guid': '2', 'context': 'context', 'version': '1', 'license': ['GPL'], 'stability': 'stable'}) @@ -205,7 +204,7 @@ class CacheTest(tests.Test): cache.recycle() def test_checkin(self): - volume = db.Volume('db', [Context, Release]) + volume = db.Volume('db', [Context]) cache = Cache(volume) volume['release'].create({'guid': '1', 'context': 'context', 'version': '1', 'license': ['GPL'], 'stability': 'stable'}) @@ -229,7 +228,7 @@ class CacheTest(tests.Test): conn = IPCConnection() self.statvfs.f_blocks = 0 - impl1 = conn.upload(['release'], StringIO(self.zips(['TestActivity/activity/activity.info', [ + bundle = self.zips(['TestActivity/activity/activity.info', [ '[Activity]', 'name = TestActivity', 'bundle_id = context', @@ -238,9 +237,12 @@ class CacheTest(tests.Test): 'activity_version = 1', 'license = Public Domain', 'stability = stable', - ]])), cmd='submit', initial=True) - + ]]) + impl1 = conn.upload(['release'], StringIO(bundle), cmd='submit', initial=True) + print self.blobs[str(hash(bundle))] conn.put(['context', 'context'], True, cmd='clone') + print self.blobs[str(hash(bundle))] + return self.assertEqual([], [i for i in self.client_routes._cache]) assert local_volume['release'].exists(impl1) @@ -271,7 +273,7 @@ class CacheTest(tests.Test): assert local_volume['release'].exists(impl2) def test_Acquiring(self): - volume = db.Volume('db', [Context, Release]) + volume = db.Volume('db', [Context]) cache = Cache(volume) volume['release'].create({'guid': '1', 'context': 'context', 'version': '1', 'license': ['GPL'], 'stability': 'stable'}) diff --git a/tests/units/client/routes.py b/tests/units/client/routes.py index 0b757f5..9fad249 100755 --- a/tests/units/client/routes.py +++ b/tests/units/client/routes.py @@ -16,7 +16,7 @@ from sugar_network.client.routes import ClientRoutes, CachedClientRoutes from sugar_network.model.user import User from sugar_network.model.report import Report from sugar_network.toolkit.router import Router, Request, Response -from sugar_network.toolkit import coroutine +from sugar_network.toolkit import coroutine, i18n import requests @@ -420,28 +420,28 @@ class RoutesTest(tests.Test): 'description': '', }) - toolkit._default_langs = None + i18n._default_langs = None os.environ['LANGUAGE'] = 'es:ru:en' ipc = IPCConnection() self.assertEqual('3', ipc.get(['context', guid1, 'title'])) self.assertEqual('2', ipc.get(['context', guid2, 'title'])) self.assertEqual('1', ipc.get(['context', guid3, 'title'])) - toolkit._default_langs = None + i18n._default_langs = None os.environ['LANGUAGE'] = 'ru:en' ipc = IPCConnection() self.assertEqual('2', ipc.get(['context', guid1, 'title'])) self.assertEqual('2', ipc.get(['context', guid2, 'title'])) self.assertEqual('1', ipc.get(['context', guid3, 'title'])) - toolkit._default_langs = None + i18n._default_langs = None os.environ['LANGUAGE'] = 'en' ipc = IPCConnection() self.assertEqual('1', ipc.get(['context', guid1, 'title'])) self.assertEqual('1', ipc.get(['context', guid2, 'title'])) self.assertEqual('1', ipc.get(['context', guid3, 'title'])) - toolkit._default_langs = None + i18n._default_langs = None os.environ['LANGUAGE'] = 'foo' ipc = IPCConnection() self.assertEqual('1', ipc.get(['context', guid1, 'title'])) diff --git a/tests/units/db/__main__.py b/tests/units/db/__main__.py index fc91d7c..3b1b9ec 100644 --- a/tests/units/db/__main__.py +++ b/tests/units/db/__main__.py @@ -2,11 +2,12 @@ from __init__ import tests -from resource import * -from index import * -#from migrate import * +from metadata import * from storage import * +from index import * +from resource import * from routes import * +#from migrate import * if __name__ == '__main__': tests.main() diff --git a/tests/units/db/files.py b/tests/units/db/files.py new file mode 100755 index 0000000..0d806df --- /dev/null +++ b/tests/units/db/files.py @@ -0,0 +1,320 @@ + + def test_diff_WithBlobsSetByUrl(self): + URL = 'http://src.sugarlabs.org/robots.txt' + URL_content = urllib2.urlopen(URL).read() + + class Document(db.Resource): + + @db.blob_property() + def blob(self, value): + return value + + directory = Directory(tests.tmpdir, Document, IndexWriter) + + directory.create({'guid': '1', 'ctime': 1, 'mtime': 1}) + directory.update('1', {'blob': {'url': URL}}) + self.utime('1/1', 1) + + out_seq = Sequence() + self.assertEqual([ + {'guid': '1', 'diff': { + 'guid': {'value': '1', 'mtime': 1}, + 'ctime': {'value': 1, 'mtime': 1}, + 'mtime': {'value': 1, 'mtime': 1}, + 'blob': { + 'url': URL, + 'mtime': 1, + }, + }}, + ], + [i for i in diff(directory, [[0, None]], out_seq)]) + self.assertEqual([[1, 2]], out_seq) + + def test_merge_AvoidCalculatedBlobs(self): + + class Document(db.Resource): + + @db.blob_property() + def blob(self, value): + return {'url': 'http://foo/bar', 'mime_type': 'image/png'} + + directory1 = Directory('document1', Document, IndexWriter) + directory1.create({'guid': 'guid', 'ctime': 1, 'mtime': 1}) + for i in os.listdir('document1/gu/guid'): + os.utime('document1/gu/guid/%s' % i, (1, 1)) + + directory2 = Directory('document2', Document, IndexWriter) + for patch in diff(directory1, [[0, None]], Sequence()): + directory2.merge(**patch) + + doc = directory2.get('guid') + self.assertEqual(1, doc.get('seqno')) + self.assertEqual(1, doc.meta('guid')['mtime']) + assert not exists('document2/gu/guid/blob') + + def test_merge_Blobs(self): + + class Document(db.Resource): + + @db.blob_property() + def blob(self, value): + return value + + directory = Directory('document', Document, IndexWriter) + self.touch(('blob', 'blob-1')) + directory.merge('1', { + 'guid': {'mtime': 1, 'value': '1'}, + 'ctime': {'mtime': 2, 'value': 2}, + 'mtime': {'mtime': 3, 'value': 3}, + 'blob': {'mtime': 4, 'blob': 'blob'}, + }) + + self.assertEqual( + [(2, 3, '1')], + [(i['ctime'], i['mtime'], i['guid']) for i in directory.find()[0]]) + + doc = directory.get('1') + self.assertEqual(1, doc.get('seqno')) + self.assertEqual(1, doc.meta('guid')['mtime']) + self.assertEqual(2, doc.meta('ctime')['mtime']) + self.assertEqual(3, doc.meta('mtime')['mtime']) + self.assertEqual(4, doc.meta('blob')['mtime']) + self.assertEqual('blob-1', file('document/1/1/blob.blob').read()) + + self.touch(('blob', 'blob-2')) + directory.merge('1', { + 'blob': {'mtime': 5, 'blob': 'blob'}, + }) + + self.assertEqual(5, doc.meta('blob')['mtime']) + self.assertEqual('blob-2', file('document/1/1/blob.blob').read()) + + + def test_DeleteOldBlobOnUpdate(self): + + class Document(db.Resource): + + @db.blob_property() + def blob(self, value): + return value + + directory = Directory(tests.tmpdir, Document, IndexWriter) + + directory.create({'guid': 'guid', 'blob': 'foo'}) + assert exists('gu/guid/blob.blob') + directory.update('guid', {'blob': {'url': 'foo'}}) + assert not exists('gu/guid/blob.blob') + + directory.update('guid', {'blob': 'foo'}) + assert exists('gu/guid/blob.blob') + directory.update('guid', {'blob': {}}) + assert not exists('gu/guid/blob.blob') + + def test_diff_Blobs(self): + + class Document(db.Resource): + + @db.blob_property() + def prop(self, value): + return value + + volume = db.Volume('db', [Document]) + cp = NodeRoutes('guid', volume) + + guid = call(cp, method='POST', document='document', content={}) + call(cp, method='PUT', document='document', guid=guid, content={'prop': 'payload'}) + self.utime('db', 0) + + patch = diff(volume, toolkit.Sequence([[1, None]])) + self.assertEqual( + {'resource': 'document'}, + next(patch)) + record = next(patch) + self.assertEqual('payload', ''.join([i for i in record.pop('blob')])) + self.assertEqual( + {'guid': guid, 'blob_size': len('payload'), 'diff': { + 'prop': { + 'digest': hashlib.sha1('payload').hexdigest(), + 'blob_size': len('payload'), + 'mime_type': 'application/octet-stream', + 'mtime': 0, + }, + }}, + record) + self.assertEqual( + {'guid': guid, 'diff': { + 'guid': {'value': guid, 'mtime': 0}, + 'author': {'mtime': 0, 'value': {}}, + 'layer': {'mtime': 0, 'value': []}, + 'tags': {'mtime': 0, 'value': []}, + 'mtime': {'value': 0, 'mtime': 0}, + 'ctime': {'value': 0, 'mtime': 0}, + }}, + next(patch)) + self.assertEqual( + {'commit': [[1, 2]]}, + next(patch)) + self.assertRaises(StopIteration, next, patch) + + def test_diff_BlobUrls(self): + url = 'http://src.sugarlabs.org/robots.txt' + blob = urllib2.urlopen(url).read() + + class Document(db.Resource): + + @db.blob_property() + def prop(self, value): + return value + + volume = db.Volume('db', [Document]) + cp = NodeRoutes('guid', volume) + + guid = call(cp, method='POST', document='document', content={}) + call(cp, method='PUT', document='document', guid=guid, content={'prop': {'url': url}}) + self.utime('db', 1) + + self.assertEqual([ + {'resource': 'document'}, + {'guid': guid, + 'diff': { + 'guid': {'value': guid, 'mtime': 1}, + 'author': {'mtime': 1, 'value': {}}, + 'layer': {'mtime': 1, 'value': []}, + 'tags': {'mtime': 1, 'value': []}, + 'mtime': {'value': 0, 'mtime': 1}, + 'ctime': {'value': 0, 'mtime': 1}, + 'prop': {'url': url, 'mtime': 1}, + }, + }, + {'commit': [[1, 2]]}, + ], + [i for i in diff(volume, toolkit.Sequence([[1, None]]))]) + + patch = diff(volume, toolkit.Sequence([[1, None]]), fetch_blobs=True) + self.assertEqual( + {'resource': 'document'}, + next(patch)) + record = next(patch) + self.assertEqual(blob, ''.join([i for i in record.pop('blob')])) + self.assertEqual( + {'guid': guid, 'blob_size': len(blob), 'diff': {'prop': {'mtime': 1}}}, + record) + self.assertEqual( + {'guid': guid, 'diff': { + 'guid': {'value': guid, 'mtime': 1}, + 'author': {'mtime': 1, 'value': {}}, + 'layer': {'mtime': 1, 'value': []}, + 'tags': {'mtime': 1, 'value': []}, + 'mtime': {'value': 0, 'mtime': 1}, + 'ctime': {'value': 0, 'mtime': 1}, + }}, + next(patch)) + self.assertEqual( + {'commit': [[1, 2]]}, + next(patch)) + self.assertRaises(StopIteration, next, patch) + + def test_diff_SkipBrokenBlobUrls(self): + + class Document(db.Resource): + + @db.blob_property() + def prop(self, value): + return value + + volume = db.Volume('db', [Document]) + cp = NodeRoutes('guid', volume) + + guid1 = call(cp, method='POST', document='document', content={}) + call(cp, method='PUT', document='document', guid=guid1, content={'prop': {'url': 'http://foo/bar'}}) + guid2 = call(cp, method='POST', document='document', content={}) + self.utime('db', 1) + + self.assertEqual([ + {'resource': 'document'}, + {'guid': guid1, + 'diff': { + 'guid': {'value': guid1, 'mtime': 1}, + 'author': {'mtime': 1, 'value': {}}, + 'layer': {'mtime': 1, 'value': []}, + 'tags': {'mtime': 1, 'value': []}, + 'mtime': {'value': 0, 'mtime': 1}, + 'ctime': {'value': 0, 'mtime': 1}, + 'prop': {'url': 'http://foo/bar', 'mtime': 1}, + }, + }, + {'guid': guid2, + 'diff': { + 'guid': {'value': guid2, 'mtime': 1}, + 'author': {'mtime': 1, 'value': {}}, + 'layer': {'mtime': 1, 'value': []}, + 'tags': {'mtime': 1, 'value': []}, + 'mtime': {'value': 0, 'mtime': 1}, + 'ctime': {'value': 0, 'mtime': 1}, + }, + }, + {'commit': [[1, 3]]}, + ], + [i for i in diff(volume, toolkit.Sequence([[1, None]]), fetch_blobs=False)]) + + self.assertEqual([ + {'resource': 'document'}, + {'guid': guid1, + 'diff': { + 'guid': {'value': guid1, 'mtime': 1}, + 'author': {'mtime': 1, 'value': {}}, + 'layer': {'mtime': 1, 'value': []}, + 'tags': {'mtime': 1, 'value': []}, + 'mtime': {'value': 0, 'mtime': 1}, + 'ctime': {'value': 0, 'mtime': 1}, + }, + }, + {'guid': guid2, + 'diff': { + 'guid': {'value': guid2, 'mtime': 1}, + 'author': {'mtime': 1, 'value': {}}, + 'layer': {'mtime': 1, 'value': []}, + 'tags': {'mtime': 1, 'value': []}, + 'mtime': {'value': 0, 'mtime': 1}, + 'ctime': {'value': 0, 'mtime': 1}, + }, + }, + {'commit': [[1, 3]]}, + ], + [i for i in diff(volume, toolkit.Sequence([[1, None]]), fetch_blobs=True)]) + + def test_merge_Blobs(self): + + class Document(db.Resource): + + @db.blob_property() + def prop(self, value): + return value + + volume = db.Volume('db', [Document]) + + merge(volume, [ + {'resource': 'document'}, + {'guid': '1', 'diff': { + 'guid': {'value': '1', 'mtime': 1.0}, + 'ctime': {'value': 2, 'mtime': 2.0}, + 'mtime': {'value': 3, 'mtime': 3.0}, + 'prop': { + 'blob': StringIO('payload'), + 'blob_size': len('payload'), + 'digest': hashlib.sha1('payload').hexdigest(), + 'mime_type': 'foo/bar', + 'mtime': 1, + }, + }}, + {'commit': [[1, 1]]}, + ]) + + assert volume['document'].exists('1') + blob = volume['document'].get('1')['prop'] + self.assertEqual(1, blob['mtime']) + self.assertEqual('foo/bar', blob['mime_type']) + self.assertEqual(hashlib.sha1('payload').hexdigest(), blob['digest']) + self.assertEqual(tests.tmpdir + '/db/document/1/1/prop.blob', blob['blob']) + self.assertEqual('payload', file(blob['blob']).read()) + diff --git a/tests/units/db/index.py b/tests/units/db/index.py index 9d996b0..cb144c6 100755 --- a/tests/units/db/index.py +++ b/tests/units/db/index.py @@ -12,23 +12,22 @@ from __init__ import tests from sugar_network import toolkit from sugar_network.db import index -from sugar_network.db.index import _fmt_prop_value -from sugar_network.db.metadata import Metadata, IndexedProperty, GUID_PREFIX, Property +from sugar_network.db.metadata import Metadata, Property, GUID_PREFIX, Boolean, Enum, List, Localized, Numeric from sugar_network.toolkit.router import ACL -from sugar_network.toolkit import coroutine +from sugar_network.toolkit import coroutine, i18n class IndexTest(tests.Test): def test_Term_AvoidCollisionsWithGuid(self): - self.assertRaises(RuntimeError, IndexedProperty, 'key', 0, 'I') - self.assertRaises(RuntimeError, IndexedProperty, 'key', 0, 'K') - self.assertRaises(RuntimeError, IndexedProperty, 'key', 1, 'I') - IndexedProperty('key', 1, 'K') - IndexedProperty('guid', 0, 'I') + self.assertRaises(RuntimeError, Property, 'key', 0, 'I') + self.assertRaises(RuntimeError, Property, 'key', 0, 'K') + self.assertRaises(RuntimeError, Property, 'key', 1, 'I') + Property('key', 1, 'K') + Property('guid', 0, 'I') def test_Create(self): - db = Index({'key': IndexedProperty('key', 1, 'K')}) + db = Index({'key': Property('key', 1, 'K')}) self.assertEqual( ([], 0), @@ -47,8 +46,8 @@ class IndexTest(tests.Test): def test_update(self): db = Index({ - 'var_1': IndexedProperty('var_1', 1, 'A'), - 'var_2': IndexedProperty('var_2', 2, 'B'), + 'var_1': Property('var_1', 1, 'A'), + 'var_2': Property('var_2', 2, 'B'), }) db.store('1', {'var_1': 'value_1', 'var_2': 'value_2'}) @@ -62,7 +61,7 @@ class IndexTest(tests.Test): db._find(reply=['var_1', 'var_2'])) def test_delete(self): - db = Index({'key': IndexedProperty('key', 1, 'K')}) + db = Index({'key': Property('key', 1, 'K')}) db.store('1', {'key': 'value'}) self.assertEqual( @@ -74,8 +73,17 @@ class IndexTest(tests.Test): ([], 0), db._find(reply=['key'])) - def test_IndexByFmt(self): - db = Index({'key': IndexedProperty('key', 1, 'K', fmt=lambda x: "foo" + x)}) + def test_IndexCalculatedValue(self): + + class Property2(Property): + + def encode(self, value): + yield "foo" + value + + def decode(self, value): + return "foo" + value + + db = Index({'key': Property2('key', 1, 'K')}) db.store('1', {'key': 'bar'}) @@ -92,15 +100,17 @@ class IndexTest(tests.Test): [], db._find(key='fake', reply=['key'])[0]) - def test_IndexByFmtGenerator(self): + def test_IndexCalculatedValues(self): - def iterate(value): - if value != 'fake': - yield 'foo' - yield 'bar' - yield value + class Property2(Property): - db = Index({'key': IndexedProperty('key', 1, 'K', fmt=iterate)}) + def encode(self, value): + if value != 'fake': + yield 'foo' + yield 'bar' + yield value + + db = Index({'key': Property2('key', 1, 'K')}) db.store('1', {'key': 'value'}) self.assertEqual( @@ -118,9 +128,9 @@ class IndexTest(tests.Test): def test_find(self): db = Index({ - 'var_1': IndexedProperty('var_1', 1, 'A', full_text=True), - 'var_2': IndexedProperty('var_2', 2, 'B', full_text=True), - 'var_3': IndexedProperty('var_3', 3, 'C', full_text=True), + 'var_1': Property('var_1', 1, 'A', full_text=True), + 'var_2': Property('var_2', 2, 'B', full_text=True), + 'var_3': Property('var_3', 3, 'C', full_text=True), }) db.store('1', {'var_1': '1', 'var_2': 'у', 'var_3': 'г'}) @@ -149,7 +159,7 @@ class IndexTest(tests.Test): def test_find_NoneFilters(self): db = Index({ - 'prop': IndexedProperty('prop', 1, 'P', full_text=True), + 'prop': Property('prop', 1, 'P', full_text=True), }) db.store('guid', {'prop': 'value'}) @@ -164,9 +174,9 @@ class IndexTest(tests.Test): [{'guid': 'guid', 'prop': 'value'}], db._find(guid=None, reply=['prop'])[0]) - def test_find_WithTypeCast(self): + def test_find_DecodeArgs(self): db = Index({ - 'var_1': IndexedProperty('var_1', 1, 'A', typecast=bool), + 'var_1': Boolean('var_1', 1, 'A'), }) db.store('1', {'var_1': True}) @@ -181,9 +191,9 @@ class IndexTest(tests.Test): def test_find_WithProps(self): db = Index({ - 'var_1': IndexedProperty('var_1', 1, 'A', full_text=True), - 'var_2': IndexedProperty('var_2', 2, 'B', full_text=True), - 'var_3': IndexedProperty('var_3', 3, 'C', full_text=True), + 'var_1': Property('var_1', 1, 'A', full_text=True), + 'var_2': Property('var_2', 2, 'B', full_text=True), + 'var_3': Property('var_3', 3, 'C', full_text=True), }) db.store('1', {'var_1': '1', 'var_2': 'у', 'var_3': 'г'}) @@ -209,9 +219,9 @@ class IndexTest(tests.Test): def test_find_WithAllBooleanProps(self): db = Index({ - 'var_1': IndexedProperty('var_1', 1, 'A', boolean=True, full_text=True), - 'var_2': IndexedProperty('var_2', 2, 'B', boolean=True, full_text=True), - 'var_3': IndexedProperty('var_3', 3, 'C', boolean=True, full_text=True), + 'var_1': Property('var_1', 1, 'A', boolean=True, full_text=True), + 'var_2': Property('var_2', 2, 'B', boolean=True, full_text=True), + 'var_3': Property('var_3', 3, 'C', boolean=True, full_text=True), }) db.store('1', {'var_1': '1', 'var_2': 'у', 'var_3': 'г'}) @@ -232,9 +242,9 @@ class IndexTest(tests.Test): def test_find_WithBooleanProps(self): db = Index({ - 'var_1': IndexedProperty('var_1', 1, 'A', boolean=True, full_text=True), - 'var_2': IndexedProperty('var_2', 2, 'B', boolean=False, full_text=True), - 'var_3': IndexedProperty('var_3', 3, 'C', boolean=True, full_text=True), + 'var_1': Property('var_1', 1, 'A', boolean=True, full_text=True), + 'var_2': Property('var_2', 2, 'B', boolean=False, full_text=True), + 'var_3': Property('var_3', 3, 'C', boolean=True, full_text=True), }) db.store('1', {'var_1': '1', 'var_2': 'у', 'var_3': 'г'}) @@ -254,7 +264,7 @@ class IndexTest(tests.Test): db._find(query='б', var_1='1', var_2='у', var_3='г', reply=['var_1'])) def test_find_ExactQuery(self): - db = Index({'key': IndexedProperty('key', 1, 'K', full_text=True)}) + db = Index({'key': Property('key', 1, 'K', full_text=True)}) db.store('1', {'key': 'фу'}) db.store('2', {'key': 'фу бар'}) @@ -280,7 +290,7 @@ class IndexTest(tests.Test): def test_find_ExactQueryTerms(self): term = 'azAZ09_' - db = Index({term: IndexedProperty(term, 1, 'T', full_text=True)}) + db = Index({term: Property(term, 1, 'T', full_text=True)}) db.store('1', {term: 'test'}) db.store('2', {term: 'test fail'}) @@ -290,7 +300,7 @@ class IndexTest(tests.Test): db._find(query='%s:=test' % term, reply=['guid'])) def test_find_ReturnPortions(self): - db = Index({'key': IndexedProperty('key', 1, 'K')}) + db = Index({'key': Property('key', 1, 'K')}) db.store('1', {'key': '1'}) db.store('2', {'key': '2'}) @@ -311,8 +321,8 @@ class IndexTest(tests.Test): def test_find_OrderBy(self): db = Index({ - 'var_1': IndexedProperty('var_1', 1, 'A'), - 'var_2': IndexedProperty('var_2', 2, 'B'), + 'var_1': Property('var_1', 1, 'A'), + 'var_2': Property('var_2', 2, 'B'), }) db.store('1', {'var_1': '1', 'var_2': '3'}) @@ -341,15 +351,15 @@ class IndexTest(tests.Test): def test_find_GroupBy(self): db = Index({ - 'var_1': IndexedProperty('var_1', 1, 'A'), - 'var_2': IndexedProperty('var_2', 2, 'B'), - 'var_3': IndexedProperty('var_3', 3, 'C'), - 'var_4': IndexedProperty('var_4', 4, 'D'), + 'var_1': Property('var_1', 1, 'A'), + 'var_2': Property('var_2', 2, 'B'), + 'var_3': Property('var_3', 3, 'C'), + 'var_4': Property('var_4', 4, 'D'), }) - db.store('1', {'var_1': '1', 'var_2': '1', 'var_3': '3', 'var_4': 0}) - db.store('2', {'var_1': '2', 'var_2': '1', 'var_3': '4', 'var_4': 0}) - db.store('3', {'var_1': '3', 'var_2': '2', 'var_3': '4', 'var_4': 0}) + db.store('1', {'var_1': '1', 'var_2': '1', 'var_3': '3', 'var_4': '0'}) + db.store('2', {'var_1': '2', 'var_2': '1', 'var_3': '4', 'var_4': '0'}) + db.store('3', {'var_1': '3', 'var_2': '2', 'var_3': '4', 'var_4': '0'}) self.assertEqual( [{'guid': '1', 'var_1': '1'}, {'guid': '3', 'var_1': '3'}], @@ -366,7 +376,7 @@ class IndexTest(tests.Test): def test_MultipleValues(self): db = Index({ - 'prop': IndexedProperty('prop', prefix='B', typecast=[1, 2], full_text=True), + 'prop': List(name='prop', prefix='B', subtype=Enum([1, 2, 3]), full_text=True), }) db.store('1', {'prop': [1, 2]}) db.store('2', {'prop': [2, 3]}) @@ -385,7 +395,7 @@ class IndexTest(tests.Test): db.close() db = Index({ - 'prop': IndexedProperty('prop', prefix='B', typecast=[], full_text=True), + 'prop': List(name='prop', prefix='B', full_text=True), }) db.store('1', {'prop': ['a', 'b']}) db.store('2', {'prop': ['b', 'c']}) @@ -448,7 +458,7 @@ class IndexTest(tests.Test): db.close() def test_find_OrderByGUIDAllTime(self): - db = Index({'prop': IndexedProperty('prop', 1, 'P')}) + db = Index({'prop': Property('prop', 1, 'P')}) db.store('3', {'prop': '1'}) db.store('2', {'prop': '1'}) @@ -469,7 +479,7 @@ class IndexTest(tests.Test): def test_find_Region(self): term = 'azAZ09_' - db = Index({term: IndexedProperty(term, 1, 'T', full_text=True)}) + db = Index({term: Property(term, 1, 'T', full_text=True)}) db.store('1', {term: 'test'}) db.store('2', {term: 'test fail'}) @@ -479,7 +489,7 @@ class IndexTest(tests.Test): db._find(query='%s:=test' % term, reply=['guid'])) def test_find_WithListProps(self): - db = Index({'prop': IndexedProperty('prop', None, 'A', full_text=True, typecast=[])}) + db = Index({'prop': List(name='prop', prefix='A', full_text=True)}) db.store('1', {'prop': ('a', )}) db.store('2', {'prop': ('a', 'aa')}) @@ -571,10 +581,10 @@ class IndexTest(tests.Test): self.assertEqual(1, len(commits)) def test_SortLocalizedProps(self): - toolkit._default_langs = ['default_lang'] + i18n._default_langs = ['default_lang'] current_lang = locale.getdefaultlocale()[0].replace('_', '-') - db = Index({'prop': IndexedProperty('prop', 1, 'A', localized=True)}) + db = Index({'prop': Localized(name='prop', slot=1, prefix='A')}) db.store('0', {'prop': {'foo': '5'}}) db.store('1', {'prop': {current_lang: '4', 'default_lang': '1', 'foo': '3'}}) @@ -598,7 +608,7 @@ class IndexTest(tests.Test): db._find(order_by='-prop')[0]) def test_SearchByLocalizedProps(self): - db = Index({'prop': IndexedProperty('prop', 1, 'A', localized=True, full_text=True)}) + db = Index({'prop': Localized(name='prop', slot=1, prefix='A', full_text=True)}) db.store('1', {'prop': {'a': 'ё'}}) db.store('2', {'prop': {'a': 'ё', 'b': 'ю'}}) @@ -635,7 +645,7 @@ class IndexTest(tests.Test): sorted(db._find(query='prop:я')[0])) def test_find_MultipleFilter(self): - db = Index({'prop': IndexedProperty('prop', 1, 'A')}) + db = Index({'prop': Property('prop', 1, 'A')}) db.store('1', {'prop': 'a'}) db.store('2', {'prop': 'b'}) @@ -677,7 +687,7 @@ class IndexTest(tests.Test): db._find(prop=['b', 'foo', 'bar'], reply=['guid'])[0]) def test_find_AndNotFilter(self): - db = Index({'prop': IndexedProperty('prop', 1, 'A')}) + db = Index({'prop': Property('prop', 1, 'A')}) db.store('1', {'prop': 'a'}) db.store('2', {'prop': 'b'}) @@ -721,24 +731,21 @@ class IndexTest(tests.Test): ]), sorted(db._find(prop=['a', 'c'], reply=['guid'], **{'!prop': 'b'})[0])) - def test_fmt_prop_value(self): - prop = Property('prop') - self.assertEqual(['0'], [i for i in _fmt_prop_value(prop, 0)]) - self.assertEqual(['1'], [i for i in _fmt_prop_value(prop, 1)]) - self.assertEqual(['0'], [i for i in _fmt_prop_value(prop, 0)]) - self.assertEqual(['1.1'], [i for i in _fmt_prop_value(prop, 1.1)]) - self.assertEqual(['0', '1'], [i for i in _fmt_prop_value(prop, [0, 1])]) - self.assertEqual(['2', '1'], [i for i in _fmt_prop_value(prop, [2, 1])]) - self.assertEqual(['probe', 'True', '0'], [i for i in _fmt_prop_value(prop, ['probe', True, 0])]) - self.assertEqual(['True'], [i for i in _fmt_prop_value(prop, True)]) - self.assertEqual(['False'], [i for i in _fmt_prop_value(prop, False)]) + def test_find_CustomEncode(self): + db = Index({'trait': Numeric('trait', 1, 'A')}) + + db.store('1', {'trait': 1}) + db.store('2', {'trait': 2}) + db.store('11', {'trait': 11}) + + self.assertEqual([{'guid': '1'}], db._find(trait='1')[0]) + self.assertEqual([{'guid': '1'}], db._find(trait=1)[0]) - prop = Property('prop', typecast=bool) - self.assertEqual(['1'], [i for i in _fmt_prop_value(prop, True)]) - self.assertEqual(['0'], [i for i in _fmt_prop_value(prop, False)]) + self.assertEqual([{'guid': '2'}], db._find(trait='2')[0]) + self.assertEqual([{'guid': '2'}], db._find(trait=2)[0]) - prop = Property('prop', fmt=lambda x: x.keys()) - self.assertEqual(['a', '2'], [i for i in _fmt_prop_value(prop, {'a': 1, 2: 'b'})]) + self.assertEqual([{'guid': '11'}], db._find(trait='11')[0]) + self.assertEqual([{'guid': '11'}], db._find(trait=11)[0]) class Index(index.IndexWriter): @@ -750,7 +757,7 @@ class Index(index.IndexWriter): metadata = Metadata(Index) metadata.update(props) - metadata['guid'] = IndexedProperty('guid', + metadata['guid'] = Property('guid', acl=ACL.CREATE | ACL.READ, slot=0, prefix=GUID_PREFIX) diff --git a/tests/units/db/metadata.py b/tests/units/db/metadata.py new file mode 100755 index 0000000..a0ba512 --- /dev/null +++ b/tests/units/db/metadata.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python +# sugar-lint: disable + +from __init__ import tests + +from sugar_network import db + + +class MetadataTest(tests.Test): + + def test_Typecast(self): + prop = db.Numeric() + self.assertEqual(1, prop.typecast(1)) + self.assertEqual(1, prop.typecast(1.1)) + self.assertEqual(1, prop.typecast('1')) + self.assertRaises(ValueError, prop.typecast, '1.0') + self.assertRaises(ValueError, prop.typecast, '') + self.assertRaises(TypeError, prop.typecast, None) + + prop = db.Boolean() + self.assertEqual(False, prop.typecast(0)) + self.assertEqual(True, prop.typecast(1)) + self.assertEqual(True, prop.typecast(1.1)) + self.assertEqual(True, prop.typecast('1')) + self.assertEqual(False, prop.typecast('false')) + self.assertEqual(True, prop.typecast(True)) + self.assertEqual(False, prop.typecast(False)) + self.assertEqual(False, prop.typecast('False')) + self.assertEqual(False, prop.typecast('0')) + self.assertEqual(False, prop.typecast('')) + self.assertEqual(False, prop.typecast(None)) + + prop = db.List(subtype=db.Numeric()) + self.assertEqual([1], prop.typecast(1)) + self.assertEqual([], prop.typecast(None)) + self.assertRaises(ValueError, prop.typecast, '') + self.assertEqual([], prop.typecast([])) + self.assertEqual([123], prop.typecast('123')) + self.assertRaises(ValueError, prop.typecast, 'a') + self.assertEqual([123, 4, 5], prop.typecast(['123', 4, 5.6])) + + prop = db.Enum(items=[1, 2]) + self.assertRaises(ValueError, prop.typecast, 0) + self.assertRaises(TypeError, prop.typecast, None) + self.assertRaises(ValueError, prop.typecast, '') + self.assertRaises(ValueError, prop.typecast, 'A') + self.assertRaises(ValueError, prop.typecast, '3') + self.assertEqual(1, prop.typecast(1)) + self.assertEqual(2, prop.typecast(2)) + self.assertEqual(1, prop.typecast('1')) + + prop = db.List() + self.assertEqual([], prop.typecast(None)) + self.assertEqual([''], prop.typecast('')) + self.assertEqual([''], prop.typecast([''])) + self.assertEqual([], prop.typecast([])) + self.assertEqual([0], prop.typecast(0)) + self.assertEqual([''], prop.typecast('')) + self.assertEqual(['foo'], prop.typecast('foo')) + + prop = db.List(subtype=db.Enum(['A', 'B', 'C'])) + self.assertRaises(ValueError, prop.typecast, '') + self.assertRaises(ValueError, prop.typecast, ['']) + self.assertEqual([], prop.typecast([])) + self.assertEqual(['A', 'B', 'C'], prop.typecast(['A', 'B', 'C'])) + self.assertRaises(ValueError, prop.typecast, ['a']) + self.assertRaises(ValueError, prop.typecast, ['A', 'x']) + + +if __name__ == '__main__': + tests.main() diff --git a/tests/units/db/resource.py b/tests/units/db/resource.py index d09010e..ef305ec 100755 --- a/tests/units/db/resource.py +++ b/tests/units/db/resource.py @@ -23,11 +23,16 @@ from sugar_network.db import directory as directory_ from sugar_network.db.directory import Directory from sugar_network.db.index import IndexWriter from sugar_network.toolkit.router import ACL +from sugar_network.toolkit.coroutine import this from sugar_network.toolkit import http, Sequence class ResourceTest(tests.Test): + def setUp(self, fork_num=0): + tests.Test.setUp(self, fork_num) + this.broadcast = lambda x: x + def test_ActiveProperty_Slotted(self): class Document(db.Resource): @@ -345,31 +350,31 @@ class ResourceTest(tests.Test): return value directory = Directory(tests.tmpdir, Document, IndexWriter) + guid = directory.create({'guid': '1', 'prop1': '1', 'prop2': '2'}) + doc = directory.get(guid) - self.assertRaises(http.NotFound, directory.patch, 'absent', {}) - - directory.create({'guid': '1', 'prop1': '1', 'prop2': '2'}) - self.assertEqual({}, directory.patch('1', {})) - self.assertEqual({}, directory.patch('1', {'prop1': '1', 'prop2': '2'})) - self.assertEqual({'prop1': '1_'}, directory.patch('1', {'prop1': '1_', 'prop2': '2'})) - self.assertEqual({'prop1': '1_', 'prop2': '2_'}, directory.patch('1', {'prop1': '1_', 'prop2': '2_'})) + self.assertEqual({}, doc.patch({})) + self.assertEqual({}, doc.patch({'prop1': '1', 'prop2': '2'})) + self.assertEqual({'prop1': '1_'}, doc.patch({'prop1': '1_', 'prop2': '2'})) + self.assertEqual({'prop1': '1_', 'prop2': '2_'}, doc.patch({'prop1': '1_', 'prop2': '2_'})) def test_patch_LocalizedProps(self): class Document(db.Resource): - @db.indexed_property(slot=1, localized=True) + @db.indexed_property(db.Localized, slot=1) def prop(self, value): return value directory = Directory(tests.tmpdir, Document, IndexWriter) + guid = directory.create({'guid': '1', 'prop': {'ru': 'ru'}}) + doc = directory.get(guid) - directory.create({'guid': '1', 'prop': {'ru': 'ru'}}) - self.assertEqual({}, directory.patch('1', {'prop': 'ru'})) - self.assertEqual({'prop': {'ru': 'ru_'}}, directory.patch('1', {'prop': {'ru': 'ru_'}})) - self.assertEqual({'prop': {'en': 'en'}}, directory.patch('1', {'prop': {'en': 'en'}})) - self.assertEqual({'prop': {'ru': 'ru', 'en': 'en'}}, directory.patch('1', {'prop': {'ru': 'ru', 'en': 'en'}})) - self.assertEqual({'prop': {'ru': 'ru_', 'en': 'en'}}, directory.patch('1', {'prop': {'ru': 'ru_', 'en': 'en'}})) + self.assertEqual({}, doc.patch({'prop': {'ru': 'ru'}})) + self.assertEqual({'prop': {'ru': 'ru_'}}, doc.patch({'prop': {'ru': 'ru_'}})) + self.assertEqual({'prop': {'en': 'en'}}, doc.patch({'prop': {'en': 'en'}})) + self.assertEqual({'prop': {'ru': 'ru', 'en': 'en'}}, doc.patch({'prop': {'ru': 'ru', 'en': 'en'}})) + self.assertEqual({'prop': {'ru': 'ru_', 'en': 'en'}}, doc.patch({'prop': {'ru': 'ru_', 'en': 'en'}})) def test_diff(self): @@ -379,21 +384,13 @@ class ResourceTest(tests.Test): def prop(self, value): return value - @db.blob_property() - def blob(self, value): - return value - directory = Directory(tests.tmpdir, Document, IndexWriter) - self.touch(('blob', '1')) directory.create({'guid': '1', 'prop': '1', 'ctime': 1, 'mtime': 1}) - directory.update('1', {'blob': {'blob': 'blob'}}) for i in os.listdir('1/1'): os.utime('1/1/%s' % i, (1, 1)) - self.touch(('blob', '2')) directory.create({'guid': '2', 'prop': '2', 'ctime': 2, 'mtime': 2}) - directory.update('2', {'blob': {'blob': 'blob'}}) for i in os.listdir('2/2'): os.utime('2/2/%s' % i, (2, 2)) @@ -408,22 +405,12 @@ class ResourceTest(tests.Test): 'ctime': {'value': 1, 'mtime': 1}, 'prop': {'value': '1', 'mtime': 1}, 'mtime': {'value': 1, 'mtime': 1}, - 'blob': { - 'mtime': 1, - 'blob': tests.tmpdir + '/1/1/blob.blob', - 'blob_size': 1, - }, }}, {'guid': '2', 'diff': { 'guid': {'value': '2', 'mtime': 2}, 'ctime': {'value': 2, 'mtime': 2}, 'prop': {'value': '2', 'mtime': 2}, 'mtime': {'value': 2, 'mtime': 2}, - 'blob': { - 'mtime': 2, - 'blob': tests.tmpdir + '/2/2/blob.blob', - 'blob_size': 1, - }, }}, {'guid': '3', 'diff': { 'guid': {'value': '3', 'mtime': 3}, @@ -433,7 +420,7 @@ class ResourceTest(tests.Test): }}, ], [i for i in diff(directory, [[0, None]], out_seq)]) - self.assertEqual([[1, 5]], out_seq) + self.assertEqual([[1, 3]], out_seq) out_seq = Sequence() self.assertEqual([ @@ -442,26 +429,15 @@ class ResourceTest(tests.Test): 'ctime': {'value': 2, 'mtime': 2}, 'prop': {'value': '2', 'mtime': 2}, 'mtime': {'value': 2, 'mtime': 2}, - 'blob': { - 'mtime': 2, - 'blob': tests.tmpdir + '/2/2/blob.blob', - 'blob_size': 1, - }, }}, ], - [i for i in diff(directory, [[3, 4]], out_seq)]) - self.assertEqual([[3, 4]], out_seq) - - out_seq = Sequence() - self.assertEqual([ - ], - [i for i in diff(directory, [[3, 3]], out_seq)]) - self.assertEqual([], out_seq) + [i for i in diff(directory, [[2, 2]], out_seq)]) + self.assertEqual([[2, 2]], out_seq) out_seq = Sequence() self.assertEqual([ ], - [i for i in diff(directory, [[6, 100]], out_seq)]) + [i for i in diff(directory, [[4, 100]], out_seq)]) self.assertEqual([], out_seq) directory.update('2', {'prop': '22'}) self.assertEqual([ @@ -469,8 +445,8 @@ class ResourceTest(tests.Test): 'prop': {'value': '22', 'mtime': int(os.stat('2/2/prop').st_mtime)}, }}, ], - [i for i in diff(directory, [[6, 100]], out_seq)]) - self.assertEqual([[6, 6]], out_seq) + [i for i in diff(directory, [[4, 100]], out_seq)]) + self.assertEqual([[4, 4]], out_seq) def test_diff_IgnoreCalcProps(self): @@ -535,37 +511,6 @@ class ResourceTest(tests.Test): self.assertEqual([[1, 1], [4, 4]], out_seq) - def test_diff_WithBlobsSetByUrl(self): - URL = 'http://src.sugarlabs.org/robots.txt' - URL_content = urllib2.urlopen(URL).read() - - class Document(db.Resource): - - @db.blob_property() - def blob(self, value): - return value - - directory = Directory(tests.tmpdir, Document, IndexWriter) - - directory.create({'guid': '1', 'ctime': 1, 'mtime': 1}) - directory.update('1', {'blob': {'url': URL}}) - self.utime('1/1', 1) - - out_seq = Sequence() - self.assertEqual([ - {'guid': '1', 'diff': { - 'guid': {'value': '1', 'mtime': 1}, - 'ctime': {'value': 1, 'mtime': 1}, - 'mtime': {'value': 1, 'mtime': 1}, - 'blob': { - 'url': URL, - 'mtime': 1, - }, - }}, - ], - [i for i in diff(directory, [[0, None]], out_seq)]) - self.assertEqual([[1, 2]], out_seq) - def test_diff_Filter(self): class Document(db.Resource): @@ -626,7 +571,7 @@ class ResourceTest(tests.Test): class Document(db.Resource): - @db.stored_property(typecast=db.AggregatedType, default=db.AggregatedType()) + @db.stored_property(db.Aggregated) def prop(self, value): return value @@ -768,21 +713,13 @@ class ResourceTest(tests.Test): def prop(self, value): return value - @db.blob_property() - def blob(self, value): - return value - directory1 = Directory('document1', Document, IndexWriter) directory1.create({'guid': '1', 'prop': '1', 'ctime': 1, 'mtime': 1}) - self.touch(('blob', '1')) - directory1.update('1', {'blob': {'blob': 'blob'}}) for i in os.listdir('document1/1/1'): os.utime('document1/1/1/%s' % i, (1, 1)) directory1.create({'guid': '2', 'prop': '2', 'ctime': 2, 'mtime': 2}) - self.touch(('blob', '2')) - directory1.update('2', {'blob': {'blob': 'blob'}}) for i in os.listdir('document1/2/2'): os.utime('document1/2/2/%s' % i, (2, 2)) @@ -808,7 +745,6 @@ class ResourceTest(tests.Test): self.assertEqual(1, doc.meta('ctime')['mtime']) self.assertEqual(1, doc.meta('prop')['mtime']) self.assertEqual(1, doc.meta('mtime')['mtime']) - self.assertEqual(1, doc.meta('blob')['mtime']) doc = directory2.get('2') self.assertEqual(2, doc.get('seqno')) @@ -816,7 +752,6 @@ class ResourceTest(tests.Test): self.assertEqual(2, doc.meta('ctime')['mtime']) self.assertEqual(2, doc.meta('prop')['mtime']) self.assertEqual(2, doc.meta('mtime')['mtime']) - self.assertEqual(2, doc.meta('blob')['mtime']) doc = directory2.get('3') self.assertEqual(3, doc.get('seqno')) @@ -824,28 +759,25 @@ class ResourceTest(tests.Test): self.assertEqual(3, doc.meta('ctime')['mtime']) self.assertEqual(3, doc.meta('prop')['mtime']) self.assertEqual(3, doc.meta('mtime')['mtime']) - self.assertEqual(None, doc.meta('blob')) def test_merge_Update(self): class Document(db.Resource): - @db.blob_property() - def blob(self, value): + @db.stored_property(default='') + def prop(self, value): return value directory1 = Directory('document1', Document, IndexWriter) directory2 = Directory('document2', Document, IndexWriter) directory1.create({'guid': 'guid', 'ctime': 1, 'mtime': 1}) - self.touch(('blob', '1')) - directory1.update('guid', {'blob': {'blob': 'blob'}}) + directory1.update('guid', {'prop': '1'}) for i in os.listdir('document1/gu/guid'): os.utime('document1/gu/guid/%s' % i, (1, 1)) directory2.create({'guid': 'guid', 'ctime': 2, 'mtime': 2}) - self.touch(('blob', '2')) - directory2.update('guid', {'blob': {'blob': 'blob'}}) + directory2.update('guid', {'prop': '2'}) for i in os.listdir('document2/gu/guid'): os.utime('document2/gu/guid/%s' % i, (2, 2)) @@ -858,8 +790,8 @@ class ResourceTest(tests.Test): self.assertEqual(2, doc.meta('guid')['mtime']) self.assertEqual(2, doc.meta('ctime')['mtime']) self.assertEqual(2, doc.meta('mtime')['mtime']) - self.assertEqual(2, doc.meta('blob')['mtime']) - self.assertEqual('2', file('document2/gu/guid/blob.blob').read()) + self.assertEqual(2, doc.meta('prop')['mtime']) + self.assertEqual('2', doc.meta('prop')['value']) for patch in diff(directory1, [[0, None]], Sequence()): directory2.merge(**patch) @@ -872,8 +804,8 @@ class ResourceTest(tests.Test): self.assertEqual(2, doc.meta('guid')['mtime']) self.assertEqual(2, doc.meta('ctime')['mtime']) self.assertEqual(2, doc.meta('mtime')['mtime']) - self.assertEqual(2, doc.meta('blob')['mtime']) - self.assertEqual('2', file('document2/gu/guid/blob.blob').read()) + self.assertEqual(2, doc.meta('prop')['mtime']) + self.assertEqual('2', doc.meta('prop')['value']) os.utime('document1/gu/guid/mtime', (3, 3)) for patch in diff(directory1, [[0, None]], Sequence()): @@ -887,10 +819,10 @@ class ResourceTest(tests.Test): self.assertEqual(2, doc.meta('guid')['mtime']) self.assertEqual(2, doc.meta('ctime')['mtime']) self.assertEqual(3, doc.meta('mtime')['mtime']) - self.assertEqual(2, doc.meta('blob')['mtime']) - self.assertEqual('2', file('document2/gu/guid/blob.blob').read()) + self.assertEqual(2, doc.meta('prop')['mtime']) + self.assertEqual('2', doc.meta('prop')['value']) - os.utime('document1/gu/guid/blob', (4, 4)) + os.utime('document1/gu/guid/prop', (4, 4)) for patch in diff(directory1, [[0, None]], Sequence()): directory2.merge(**patch) @@ -902,132 +834,14 @@ class ResourceTest(tests.Test): self.assertEqual(2, doc.meta('guid')['mtime']) self.assertEqual(2, doc.meta('ctime')['mtime']) self.assertEqual(3, doc.meta('mtime')['mtime']) - self.assertEqual(4, doc.meta('blob')['mtime']) - self.assertEqual('1', file('document2/gu/guid/blob.blob').read()) - - def test_merge_SeqnoLessMode(self): - - class Document(db.Resource): - - @db.indexed_property(slot=1) - def prop(self, value): - return value - - directory1 = Directory('document1', Document, IndexWriter) - directory1.create({'guid': '1', 'prop': '1', 'ctime': 1, 'mtime': 1}) - - directory2 = Directory('document2', Document, IndexWriter) - for patch in diff(directory1, [[0, None]], Sequence()): - directory2.merge(shift_seqno=False, **patch) - self.assertEqual( - [(1, 1, '1', '1')], - [(i['ctime'], i['mtime'], i['guid'], i['prop']) for i in directory2.find()[0]]) - doc = directory2.get('1') - self.assertEqual(0, doc.get('seqno')) - self.assertEqual(0, doc.meta('guid')['seqno']) - self.assertEqual(0, doc.meta('prop')['seqno']) - - directory3 = Directory('document3', Document, IndexWriter) - for patch in diff(directory1, [[0, None]], Sequence()): - directory3.merge(**patch) - self.assertEqual( - [(1, 1, '1', '1')], - [(i['ctime'], i['mtime'], i['guid'], i['prop']) for i in directory3.find()[0]]) - doc = directory3.get('1') - self.assertEqual(1, doc.get('seqno')) - self.assertEqual(1, doc.meta('guid')['seqno']) - self.assertEqual(1, doc.meta('prop')['seqno']) - - time.sleep(1) - directory1.update('1', {'prop': '2', 'ctime': 2, 'mtime': 2}) - - for patch in diff(directory1, [[0, None]], Sequence()): - directory3.merge(shift_seqno=False, **patch) - self.assertEqual( - [(2, 2, '1', '2')], - [(i['ctime'], i['mtime'], i['guid'], i['prop']) for i in directory3.find()[0]]) - doc = directory3.get('1') - self.assertEqual(1, doc.get('seqno')) - self.assertEqual(1, doc.meta('guid')['seqno']) - self.assertEqual(1, doc.meta('prop')['seqno']) - - time.sleep(1) - directory1.update('1', {'prop': '3', 'ctime': 3, 'mtime': 3}) - - for patch in diff(directory1, [[0, None]], Sequence()): - directory3.merge(**patch) - self.assertEqual( - [(3, 3, '1', '3')], - [(i['ctime'], i['mtime'], i['guid'], i['prop']) for i in directory3.find()[0]]) - doc = directory3.get('1') - self.assertEqual(2, doc.get('seqno')) - self.assertEqual(1, doc.meta('guid')['seqno']) - self.assertEqual(2, doc.meta('prop')['seqno']) - - def test_merge_AvoidCalculatedBlobs(self): - - class Document(db.Resource): - - @db.blob_property() - def blob(self, value): - return {'url': 'http://foo/bar', 'mime_type': 'image/png'} - - directory1 = Directory('document1', Document, IndexWriter) - directory1.create({'guid': 'guid', 'ctime': 1, 'mtime': 1}) - for i in os.listdir('document1/gu/guid'): - os.utime('document1/gu/guid/%s' % i, (1, 1)) - - directory2 = Directory('document2', Document, IndexWriter) - for patch in diff(directory1, [[0, None]], Sequence()): - directory2.merge(**patch) - - doc = directory2.get('guid') - self.assertEqual(1, doc.get('seqno')) - self.assertEqual(1, doc.meta('guid')['mtime']) - assert not exists('document2/gu/guid/blob') - - def test_merge_Blobs(self): - - class Document(db.Resource): - - @db.blob_property() - def blob(self, value): - return value - - directory = Directory('document', Document, IndexWriter) - self.touch(('blob', 'blob-1')) - directory.merge('1', { - 'guid': {'mtime': 1, 'value': '1'}, - 'ctime': {'mtime': 2, 'value': 2}, - 'mtime': {'mtime': 3, 'value': 3}, - 'blob': {'mtime': 4, 'blob': 'blob'}, - }) - - self.assertEqual( - [(2, 3, '1')], - [(i['ctime'], i['mtime'], i['guid']) for i in directory.find()[0]]) - - doc = directory.get('1') - self.assertEqual(1, doc.get('seqno')) - self.assertEqual(1, doc.meta('guid')['mtime']) - self.assertEqual(2, doc.meta('ctime')['mtime']) - self.assertEqual(3, doc.meta('mtime')['mtime']) - self.assertEqual(4, doc.meta('blob')['mtime']) - self.assertEqual('blob-1', file('document/1/1/blob.blob').read()) - - self.touch(('blob', 'blob-2')) - directory.merge('1', { - 'blob': {'mtime': 5, 'blob': 'blob'}, - }) - - self.assertEqual(5, doc.meta('blob')['mtime']) - self.assertEqual('blob-2', file('document/1/1/blob.blob').read()) + self.assertEqual(4, doc.meta('prop')['mtime']) + self.assertEqual('1', doc.meta('prop')['value']) def test_merge_Aggprops(self): class Document(db.Resource): - @db.stored_property(typecast=db.AggregatedType, default=db.AggregatedType()) + @db.stored_property(db.Aggregated) def prop(self, value): return value @@ -1079,6 +893,28 @@ class ResourceTest(tests.Test): }, directory.get('1')['prop']) + def test_merge_CallSetters(self): + + class Document(db.Resource): + + @db.stored_property(db.Numeric) + def prop(self, value): + return value + + @prop.setter + def prop(self, value): + return value + 1 + + directory = Directory('document', Document, IndexWriter) + + directory.merge('1', { + 'guid': {'mtime': 1, 'value': '1'}, + 'ctime': {'mtime': 1, 'value': 1}, + 'mtime': {'mtime': 1, 'value': 1}, + 'prop': {'mtime': 1, 'value': 1}, + }) + self.assertEqual(2, directory.get('1')['prop']) + def test_wipe(self): class Document(db.Resource): @@ -1088,31 +924,11 @@ class ResourceTest(tests.Test): guid = directory.create({'prop': '1'}) self.assertEqual([guid], [i.guid for i in directory.find()[0]]) directory.commit() - assert directory.mtime != 0 + assert exists('index/mtime') directory.wipe() self.assertEqual([], [i.guid for i in directory.find()[0]]) - assert directory.mtime == 0 - - def test_DeleteOldBlobOnUpdate(self): - - class Document(db.Resource): - - @db.blob_property() - def blob(self, value): - return value - - directory = Directory(tests.tmpdir, Document, IndexWriter) - - directory.create({'guid': 'guid', 'blob': 'foo'}) - assert exists('gu/guid/blob.blob') - directory.update('guid', {'blob': {'url': 'foo'}}) - assert not exists('gu/guid/blob.blob') - - directory.update('guid', {'blob': 'foo'}) - assert exists('gu/guid/blob.blob') - directory.update('guid', {'blob': {}}) - assert not exists('gu/guid/blob.blob') + assert not exists('index/mtime') def diff(directory, in_seq, out_seq, exclude_seq=None, **kwargs): diff --git a/tests/units/db/routes.py b/tests/units/db/routes.py index 5908d0f..8824ca8 100755 --- a/tests/units/db/routes.py +++ b/tests/units/db/routes.py @@ -16,10 +16,11 @@ src_root = abspath(dirname(__file__)) from __init__ import tests from sugar_network import db, toolkit -from sugar_network.db.routes import _typecast_prop_value -from sugar_network.db.metadata import Property -from sugar_network.toolkit.router import Router, Request, Response, fallbackroute, Blob, ACL -from sugar_network.toolkit import coroutine, http +from sugar_network.db import files +from sugar_network.model.user import User +from sugar_network.toolkit.router import Router, Request, Response, fallbackroute, ACL +from sugar_network.toolkit.coroutine import this +from sugar_network.toolkit import coroutine, http, i18n class RoutesTest(tests.Test): @@ -36,18 +37,19 @@ class RoutesTest(tests.Test): def wo_default(self, value): return value - @db.indexed_property(slot=1, default='not_stored_default') + @db.stored_property(default='not_stored_default') def not_stored_default(self, value): return value - self.volume = db.Volume(tests.tmpdir, [Document], lambda event: None) + volume = db.Volume(tests.tmpdir, [Document]) + router = Router(db.Routes(volume)) - self.assertRaises(RuntimeError, self.call, 'POST', ['document'], content={}) + self.assertRaises(RuntimeError, this.call, method='POST', path=['document'], content={}) - guid = self.call('POST', ['document'], content={'wo_default': 'wo_default'}) - self.assertEqual('default', self.call('GET', ['document', guid, 'w_default'])) - self.assertEqual('wo_default', self.call('GET', ['document', guid, 'wo_default'])) - self.assertEqual('not_stored_default', self.call('GET', ['document', guid, 'not_stored_default'])) + guid = this.call(method='POST', path=['document'], content={'wo_default': 'wo_default'}) + self.assertEqual('default', this.call(method='GET', path=['document', guid, 'w_default'])) + self.assertEqual('wo_default', this.call(method='GET', path=['document', guid, 'wo_default'])) + self.assertEqual('not_stored_default', this.call(method='GET', path=['document', guid, 'not_stored_default'])) def test_Populate(self): self.touch( @@ -65,10 +67,10 @@ class RoutesTest(tests.Test): class Document(db.Resource): pass - with db.Volume(tests.tmpdir, [Document], lambda event: None) as volume: - for cls in volume.values(): - for __ in cls.populate(): - pass + with db.Volume(tests.tmpdir, [Document]) as volume: + router = Router(db.Routes(volume)) + for __ in volume['document'].populate(): + pass self.assertEqual( sorted(['1', '2']), sorted([i.guid for i in volume['document'].find()[0]])) @@ -78,10 +80,10 @@ class RoutesTest(tests.Test): class Document(db.Resource): pass - with db.Volume(tests.tmpdir, [Document], lambda event: None) as volume: - for cls in volume.values(): - for __ in cls.populate(): - pass + with db.Volume(tests.tmpdir, [Document]) as volume: + router = Router(db.Routes(volume)) + for __ in volume['document'].populate(): + pass self.assertEqual( sorted(['1', '2']), sorted([i.guid for i in volume['document'].find()[0]])) @@ -94,12 +96,14 @@ class RoutesTest(tests.Test): def prop(self, value): return value - @db.indexed_property(prefix='L', localized=True, default='') + @db.indexed_property(db.Localized, prefix='L', default={}) def localized_prop(self, value): return value - self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) - self.volume['testdocument'].create({'guid': 'guid'}) + volume = db.Volume(tests.tmpdir, [TestDocument]) + router = Router(db.Routes(volume)) + + volume['testdocument'].create({'guid': 'guid'}) self.assertEqual({ 'total': 1, @@ -107,11 +111,11 @@ class RoutesTest(tests.Test): {'guid': 'guid', 'prop': ''}, ], }, - self.call('GET', path=['testdocument'], reply=['guid', 'prop'])) + this.call(method='GET', path=['testdocument'], reply=['guid', 'prop'])) - guid_1 = self.call('POST', path=['testdocument'], content={'prop': 'value_1'}) + guid_1 = this.call(method='POST', path=['testdocument'], content={'prop': 'value_1'}) assert guid_1 - guid_2 = self.call('POST', path=['testdocument'], content={'prop': 'value_2'}) + guid_2 = this.call(method='POST', path=['testdocument'], content={'prop': 'value_2'}) assert guid_2 self.assertEqual( @@ -120,9 +124,9 @@ class RoutesTest(tests.Test): {'guid': guid_1, 'prop': 'value_1'}, {'guid': guid_2, 'prop': 'value_2'}, ]), - sorted(self.call('GET', path=['testdocument'], reply=['guid', 'prop'])['result'])) + sorted(this.call(method='GET', path=['testdocument'], reply=['guid', 'prop'])['result'])) - self.call('PUT', path=['testdocument', guid_1], content={'prop': 'value_3'}) + this.call(method='PUT', path=['testdocument', guid_1], content={'prop': 'value_3'}) self.assertEqual( sorted([ @@ -130,240 +134,397 @@ class RoutesTest(tests.Test): {'guid': guid_1, 'prop': 'value_3'}, {'guid': guid_2, 'prop': 'value_2'}, ]), - sorted(self.call('GET', path=['testdocument'], reply=['guid', 'prop'])['result'])) + sorted(this.call(method='GET', path=['testdocument'], reply=['guid', 'prop'])['result'])) - self.call('DELETE', path=['testdocument', guid_2]) + this.call(method='DELETE', path=['testdocument', guid_2]) self.assertEqual( sorted([ {'guid': 'guid', 'prop': ''}, {'guid': guid_1, 'prop': 'value_3'}, ]), - sorted(self.call('GET', path=['testdocument'], reply=['guid', 'prop'])['result'])) + sorted(this.call(method='GET', path=['testdocument'], reply=['guid', 'prop'])['result'])) - self.assertRaises(http.NotFound, self.call, 'GET', path=['testdocument', guid_2]) + self.assertRaises(http.NotFound, this.call, method='GET', path=['testdocument', guid_2]) self.assertEqual( {'guid': guid_1, 'prop': 'value_3'}, - self.call('GET', path=['testdocument', guid_1], reply=['guid', 'prop'])) + this.call(method='GET', path=['testdocument', guid_1], reply=['guid', 'prop'])) self.assertEqual( 'value_3', - self.call('GET', path=['testdocument', guid_1, 'prop'])) + this.call(method='GET', path=['testdocument', guid_1, 'prop'])) def test_SetBLOBs(self): class TestDocument(db.Resource): - @db.blob_property() + @db.stored_property(db.Blob) def blob(self, value): return value - self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) - guid = self.call('POST', path=['testdocument'], content={}) + volume = db.Volume(tests.tmpdir, [TestDocument]) + router = Router(db.Routes(volume)) - self.call('PUT', path=['testdocument', guid, 'blob'], content='blob1') - self.assertEqual('blob1', file(self.call('GET', path=['testdocument', guid, 'blob'])['blob']).read()) + guid = this.call(method='POST', path=['testdocument'], content={}) + self.assertRaises(http.NotFound, this.call, method='GET', path=['testdocument', guid, 'blob']) - self.call('PUT', path=['testdocument', guid, 'blob'], content_stream=StringIO('blob2')) - self.assertEqual('blob2', file(self.call('GET', path=['testdocument', guid, 'blob'])['blob']).read()) + this.call(method='PUT', path=['testdocument', guid, 'blob'], content='blob1') + self.assertEqual('blob1', file(this.call(method='GET', path=['testdocument', guid, 'blob']).path).read()) - self.call('PUT', path=['testdocument', guid, 'blob'], content=None) - self.assertRaises(http.NotFound, self.call, 'GET', path=['testdocument', guid, 'blob']) + this.call(method='PUT', path=['testdocument', guid, 'blob'], content_stream=StringIO('blob2')) + self.assertEqual('blob2', file(this.call(method='GET', path=['testdocument', guid, 'blob']).path).read()) - def test_SetBLOBsByMeta(self): + this.call(method='PUT', path=['testdocument', guid, 'blob'], content=None) + self.assertRaises(http.NotFound, this.call, method='GET', path=['testdocument', guid, 'blob']) + + def test_CreateBLOBsWithMeta(self): class TestDocument(db.Resource): - @db.blob_property(mime_type='default') + @db.stored_property(db.Blob) def blob(self, value): return value - self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) - guid = self.call('POST', path=['testdocument'], content={}) + volume = db.Volume(tests.tmpdir, [TestDocument]) + router = Router(db.Routes(volume)) + guid = this.call(method='POST', path=['testdocument'], content={}) - self.assertRaises(RuntimeError, self.call, 'PUT', path=['testdocument', guid, 'blob'], + self.assertRaises(http.BadRequest, this.call, method='PUT', path=['testdocument', guid, 'blob'], content={}, content_type='application/json') - self.assertRaises(http.NotFound, self.call, 'GET', path=['testdocument', guid, 'blob']) + self.assertRaises(http.NotFound, this.call, method='GET', path=['testdocument', guid, 'blob']) + + self.assertRaises(http.BadRequest, this.call, method='PUT', path=['testdocument', guid, 'blob'], + content={'url': 'foo'}, content_type='application/json') + self.assertRaises(http.NotFound, this.call, method='GET', path=['testdocument', guid, 'blob']) + + this.call(method='PUT', path=['testdocument', guid, 'blob'], + content={'url': 'url', 'digest': 'digest', 'foo': 'bar'}, content_type='application/json') + self.assertEqual({ + 'mime_type': 'application/octet-stream', + 'url': 'url', + 'foo': 'bar', + }, + this.call(method='GET', path=['testdocument', guid, 'blob'])) + + def test_UpdateUrlBLOBsWithMeta(self): + + class TestDocument(db.Resource): + + @db.stored_property(db.Blob) + def blob(self, value): + return value + + volume = db.Volume(tests.tmpdir, [TestDocument]) + router = Router(db.Routes(volume)) + + guid = this.call(method='POST', path=['testdocument'], content={'blob': {'digest': 'digest', 'url': 'url'}}) + self.assertEqual({ + 'mime_type': 'application/octet-stream', + 'url': 'url', + }, + this.call(method='GET', path=['testdocument', guid, 'blob'])) + + self.assertRaises(http.BadRequest, this.call, method='PUT', path=['testdocument', guid, 'blob'], + content={'digest': 'fake'}, content_type='application/json') + self.assertEqual({ + 'mime_type': 'application/octet-stream', + 'url': 'url', + }, + this.call(method='GET', path=['testdocument', guid, 'blob'])) + + this.call(method='PUT', path=['testdocument', guid, 'blob'], + content={'foo': 'bar'}, content_type='application/json') + self.assertEqual({ + 'mime_type': 'application/octet-stream', + 'url': 'url', + 'foo': 'bar', + }, + this.call(method='GET', path=['testdocument', guid, 'blob'])) + + def test_UpdateFileBLOBsWithMeta(self): + + class TestDocument(db.Resource): + + @db.stored_property(db.Blob) + def blob(self, value): + return value + + volume = db.Volume(tests.tmpdir, [TestDocument]) + router = Router(db.Routes(volume)) + + guid = this.call(method='POST', path=['testdocument'], content={'blob': 'blob'}) + blob = this.call(method='GET', path=['testdocument', guid, 'blob'], environ={'HTTP_HOST': 'localhost'}) + self.assertEqual({ + 'mime_type': 'application/octet-stream', + 'size': os.stat(blob.path).st_size, + 'mtime': int(os.stat(blob.path).st_mtime), + 'url': 'http://localhost/blobs/%s' % hash('blob'), + 'digest': str(hash('blob')), + }, + blob) + self.assertEqual('blob', file(blob.path).read()) + + self.assertRaises(http.BadRequest, this.call, method='PUT', path=['testdocument', guid, 'blob'], + content={'digest': 'fake'}, content_type='application/json') + blob = this.call(method='GET', path=['testdocument', guid, 'blob'], environ={'HTTP_HOST': 'localhost'}) + self.assertEqual({ + 'mime_type': 'application/octet-stream', + 'size': os.stat(blob.path).st_size, + 'mtime': int(os.stat(blob.path).st_mtime), + 'digest': str(hash('blob')), + 'url': 'http://localhost/blobs/%s' % hash('blob'), + }, + blob) + self.assertEqual('blob', file(blob.path).read()) + + this.call(method='PUT', path=['testdocument', guid, 'blob'], + content={'foo': 'bar'}, content_type='application/json') + blob = this.call(method='GET', path=['testdocument', guid, 'blob'], environ={'HTTP_HOST': 'localhost'}) + self.assertEqual({ + 'mime_type': 'application/octet-stream', + 'size': os.stat(blob.path).st_size, + 'mtime': int(os.stat(blob.path).st_mtime), + 'digest': str(hash('blob')), + 'url': 'http://localhost/blobs/%s' % hash('blob'), + 'foo': 'bar', + }, + blob) + self.assertEqual('blob', file(blob.path).read()) + + def test_SwitchBLOBsType(self): + + class TestDocument(db.Resource): + + @db.stored_property(db.Blob) + def blob(self, value): + return value + + volume = db.Volume(tests.tmpdir, [TestDocument]) + router = Router(db.Routes(volume)) - self.call('PUT', path=['testdocument', guid, 'blob'], - content={'url': 'foo', 'bar': 'probe'}, content_type='application/json') - blob = self.call('GET', path=['testdocument', guid, 'blob']) - self.assertEqual('foo', blob['url']) + guid = this.call(method='POST', path=['testdocument'], content={'blob': 'blob'}) + this.call(method='PUT', path=['testdocument', guid, 'blob'], + content={'foo': 'bar'}, content_type='application/json') + + file_blob = this.call(method='GET', path=['testdocument', guid, 'blob'], environ={'HTTP_HOST': 'localhost'}) + self.assertEqual({ + 'mime_type': 'application/octet-stream', + 'size': os.stat(file_blob.path).st_size, + 'mtime': int(os.stat(file_blob.path).st_mtime), + 'digest': str(hash('blob')), + 'url': 'http://localhost/blobs/%s' % hash('blob'), + 'foo': 'bar', + }, file_blob) + self.assertEqual('blob', file(file_blob.path).read()) + + this.call(method='PUT', path=['testdocument', guid, 'blob'], + content={'url': 'url'}, content_type='application/json') + self.assertEqual({ + 'mime_type': 'application/octet-stream', + 'url': 'url', + 'foo': 'bar', + }, this.call(method='GET', path=['testdocument', guid, 'blob'])) + assert not exists(file_blob.path) + + this.call(method='PUT', path=['testdocument', guid, 'blob'], + content='blob', content_type='application/octet-stream', environ={'HTTP_HOST': 'localhost'}) + self.assertEqual({ + 'mime_type': 'application/octet-stream', + 'size': os.stat(file_blob.path).st_size, + 'mtime': int(os.stat(file_blob.path).st_mtime), + 'digest': str(hash('blob')), + 'url': 'http://localhost/blobs/%s' % hash('blob'), + }, this.call(method='GET', path=['testdocument', guid, 'blob'], environ={'HTTP_HOST': 'localhost'})) + self.assertEqual('blob', file(file_blob.path).read()) def test_RemoveBLOBs(self): class TestDocument(db.Resource): - @db.blob_property(mime_type='default') + @db.stored_property(db.Blob) def blob(self, value): return value - self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) - guid = self.call('POST', path=['testdocument'], content={'blob': 'blob'}) + volume = db.Volume(tests.tmpdir, [TestDocument]) + router = Router(db.Routes(volume)) + guid = this.call(method='POST', path=['testdocument'], content={'blob': 'blob'}) - self.assertEqual('blob', file(self.call('GET', path=['testdocument', guid, 'blob'])['blob']).read()) + self.assertEqual('blob', file(this.call(method='GET', path=['testdocument', guid, 'blob']).path).read()) - self.call('PUT', path=['testdocument', guid, 'blob']) - self.assertRaises(http.NotFound, self.call, 'GET', path=['testdocument', guid, 'blob']) + this.call(method='PUT', path=['testdocument', guid, 'blob']) + self.assertRaises(http.NotFound, this.call, method='GET', path=['testdocument', guid, 'blob']) - def test_RemoveTempBLOBFilesOnFails(self): + def test_ReuploadBLOBs(self): class TestDocument(db.Resource): - @db.blob_property(mime_type='default') + @db.stored_property(db.Blob) + def blob(self, value): + return value + + volume = db.Volume(tests.tmpdir, [TestDocument]) + router = Router(db.Routes(volume)) + guid = this.call(method='POST', path=['testdocument'], content={'blob': 'blob1'}) + + blob1 = this.call(method='GET', path=['testdocument', guid, 'blob']) + self.assertEqual('blob1', file(blob1.path).read()) + + this.call(method='PUT', path=['testdocument', guid, 'blob'], content='blob2') + blob2 = this.call(method='GET', path=['testdocument', guid, 'blob']) + self.assertEqual('blob2', file(blob2.path).read()) + assert blob1.path != blob2.path + assert not exists(blob1.path) + + def test_RemoveBLOBsOnFailedSetter(self): + + class TestDocument(db.Resource): + + @db.stored_property(db.Blob) def blob(self, value): return value @blob.setter def blob(self, value): - raise RuntimeError() + if value: + raise RuntimeError() + return value + + volume = db.Volume(tests.tmpdir, [TestDocument]) + router = Router(db.Routes(volume)) - self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) - guid = self.call('POST', path=['testdocument'], content={}) + guid = this.call(method='POST', path=['testdocument'], content={}) + self.assertRaises(http.NotFound, this.call, method='GET', path=['testdocument', guid, 'blob']) - self.assertRaises(RuntimeError, self.call, 'PUT', path=['testdocument', guid, 'blob'], content='probe') - self.assertEqual(0, len(os.listdir('tmp'))) + self.assertRaises(RuntimeError, this.call, method='PUT', path=['testdocument', guid, 'blob'], content='probe') + self.assertRaises(http.NotFound, this.call, method='GET', path=['testdocument', guid, 'blob']) + assert not exists('blobs/%s' % hash('probe')) def test_SetBLOBsWithMimeType(self): class TestDocument(db.Resource): - @db.blob_property(mime_type='default') + @db.stored_property(db.Blob, mime_type='default') def blob(self, value): return value - self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) - guid = self.call('POST', path=['testdocument'], content={}) + volume = db.Volume(tests.tmpdir, [TestDocument]) + router = Router(db.Routes(volume)) + guid = this.call(method='POST', path=['testdocument'], content={}) - self.call('PUT', path=['testdocument', guid, 'blob'], content='blob1') - self.assertEqual('default', self.call('GET', path=['testdocument', guid, 'blob'])['mime_type']) - self.assertEqual('default', self.response.content_type) + response = Response() + this.call(response=response, + method='PUT', path=['testdocument', guid, 'blob'], content='blob1') + response = Response() + self.assertEqual('default', this.call(response=response, + method='GET', path=['testdocument', guid, 'blob'])['mime_type']) + self.assertEqual('default', response.content_type) - self.call('PUT', path=['testdocument', guid, 'blob'], content='blob1', content_type='foo') - self.assertEqual('foo', self.call('GET', path=['testdocument', guid, 'blob'])['mime_type']) - self.assertEqual('foo', self.response.content_type) + response = Response() + this.call(response=response, + method='PUT', path=['testdocument', guid, 'blob'], content='blob1', content_type='foo') + response = Response() + self.assertEqual('foo', this.call(response=response, + method='GET', path=['testdocument', guid, 'blob'])['mime_type']) + self.assertEqual('foo', response.content_type) def test_GetBLOBs(self): class TestDocument(db.Resource): - @db.blob_property() + @db.stored_property(db.Blob) def blob(self, value): return value - self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) - guid = self.call('POST', path=['testdocument'], content={}) + volume = db.Volume(tests.tmpdir, [TestDocument]) + router = Router(db.Routes(volume)) + + guid = this.call(method='POST', path=['testdocument'], content={}) blob = 'blob' - self.call('PUT', path=['testdocument', guid, 'blob'], content=blob) - - blob_path = tests.tmpdir + '/testdocument/%s/%s/blob' % (guid[:2], guid) - blob_meta = { - 'seqno': 2, - 'blob': blob_path + '.blob', - 'blob_size': len(blob), - 'digest': hashlib.sha1(blob).hexdigest(), - 'mime_type': 'application/octet-stream', - 'mtime': int(os.stat(blob_path).st_mtime), - } + this.call(method='PUT', path=['testdocument', guid, 'blob'], content=blob) + digest = str(hash(blob)) + blob_path = 'blobs/%s' % digest - self.assertEqual('blob', file(self.call('GET', path=['testdocument', guid, 'blob'])['blob']).read()) + self.assertEqual('blob', file(this.call(method='GET', path=['testdocument', guid, 'blob']).path).read()) self.assertEqual({ 'blob': { - 'url': 'http://localhost/testdocument/%s/blob' % guid, - 'blob_size': len(blob), - 'digest': hashlib.sha1(blob).hexdigest(), 'mime_type': u'application/octet-stream', + 'url': 'http://localhost/blobs/%s' % digest, + 'size': len(blob), + 'digest': digest, + 'mtime': int(os.stat(blob_path).st_mtime), }, }, - self.call('GET', path=['testdocument', guid], reply=['blob'], host='localhost')) + this.call(method='GET', path=['testdocument', guid], reply=['blob'], environ={'HTTP_HOST': 'localhost'})) self.assertEqual([{ 'blob': { - 'url': 'http://localhost/testdocument/%s/blob' % guid, - 'blob_size': len(blob), - 'digest': hashlib.sha1(blob).hexdigest(), 'mime_type': u'application/octet-stream', + 'url': 'http://localhost/blobs/%s' % digest, + 'size': len(blob), + 'digest': digest, + 'mtime': int(os.stat(blob_path).st_mtime), }, }], - self.call('GET', path=['testdocument'], reply=['blob'], host='localhost')['result']) + this.call(method='GET', path=['testdocument'], reply=['blob'], environ={'HTTP_HOST': 'localhost'})['result']) def test_GetBLOBsByUrls(self): class TestDocument(db.Resource): - @db.blob_property() + @db.stored_property(db.Blob) def blob(self, value): return value - self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) - guid1 = self.call('POST', path=['testdocument'], content={}) + volume = db.Volume(tests.tmpdir, [TestDocument]) + router = Router(db.Routes(volume)) + guid1 = this.call(method='POST', path=['testdocument'], content={}) - self.assertRaises(http.NotFound, self.call, 'GET', path=['testdocument', guid1, 'blob']) + self.assertRaises(http.NotFound, this.call, method='GET', path=['testdocument', guid1, 'blob']) self.assertEqual( - {'blob': {'url': 'http://127.0.0.1/testdocument/%s/blob' % guid1}}, - self.call('GET', path=['testdocument', guid1], reply=['blob'], host='127.0.0.1')) + {'blob': {}}, + this.call(method='GET', path=['testdocument', guid1], reply=['blob'], environ={'HTTP_HOST': '127.0.0.1'})) blob = 'file' - guid2 = self.call('POST', path=['testdocument'], content={'blob': blob}) - self.assertEqual('file', file(self.call('GET', path=['testdocument', guid2, 'blob'])['blob']).read()) - self.assertEqual({ - 'blob': { - 'url': 'http://127.0.0.1/testdocument/%s/blob' % guid2, - 'blob_size': len(blob), - 'digest': hashlib.sha1(blob).hexdigest(), - 'mime_type': u'application/octet-stream', - }, - }, - self.call('GET', path=['testdocument', guid2], reply=['blob'], host='127.0.0.1')) + guid2 = this.call(method='POST', path=['testdocument'], content={'blob': blob}) + self.assertEqual( + 'http://127.0.0.1/blobs/%s' % hash(blob), + this.call(method='GET', path=['testdocument', guid2], reply=['blob'], environ={'HTTP_HOST': '127.0.0.1'})['blob']['url']) - guid3 = self.call('POST', path=['testdocument'], content={'blob': {'url': 'http://foo'}}, content_type='application/json') - self.assertEqual('http://foo', self.call('GET', path=['testdocument', guid3, 'blob'])['url']) - self.assertEqual({ - 'blob': { - 'url': 'http://foo', - }, - }, - self.call('GET', path=['testdocument', guid3], reply=['blob'], host='127.0.0.1')) + guid3 = this.call(method='POST', path=['testdocument'], content={'blob': {'url': 'http://foo', 'digest': 'digest'}}, content_type='application/json') + self.assertEqual( + 'http://foo', + this.call(method='GET', path=['testdocument', guid3, 'blob'])['url']) + self.assertEqual( + 'http://foo', + this.call(method='GET', path=['testdocument', guid3], reply=['blob'], environ={'HTTP_HOST': '127.0.0.1'})['blob']['url']) self.assertEqual( sorted([ - {'blob': { - 'url': 'http://127.0.0.1/testdocument/%s/blob' % guid1, - }}, - { 'blob': { - 'url': 'http://127.0.0.1/testdocument/%s/blob' % guid2, - 'blob_size': len(blob), - 'digest': hashlib.sha1(blob).hexdigest(), - 'mime_type': u'application/octet-stream', - }}, - { 'blob': { - 'url': 'http://foo', - }}, + None, + 'http://127.0.0.1/blobs/%s' % hash(blob), + 'http://foo', ]), - sorted(self.call('GET', path=['testdocument'], reply=['blob'], host='127.0.0.1')['result'])) + sorted([i['blob'].get('url') for i in this.call(method='GET', path=['testdocument'], reply=['blob'], + environ={'HTTP_HOST': '127.0.0.1'})['result']])) def test_CommandsGetAbsentBlobs(self): class TestDocument(db.Resource): - @db.indexed_property(slot=1, default='') - def prop(self, value): - return value - - @db.blob_property() + @db.stored_property(db.Blob) def blob(self, value): return value - self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) + volume = db.Volume(tests.tmpdir, [TestDocument]) + router = Router(db.Routes(volume)) - guid = self.call('POST', path=['testdocument'], content={'prop': 'value'}) - self.assertEqual('value', self.call('GET', path=['testdocument', guid, 'prop'])) - self.assertRaises(http.NotFound, self.call, 'GET', path=['testdocument', guid, 'blob']) + guid = this.call(method='POST', path=['testdocument'], content={}) + self.assertRaises(http.NotFound, this.call, method='GET', path=['testdocument', guid, 'blob']) self.assertEqual( - {'blob': {'url': 'http://localhost/testdocument/%s/blob' % guid}}, - self.call('GET', path=['testdocument', guid], reply=['blob'], host='localhost')) + {'blob': {}}, + this.call(method='GET', path=['testdocument', guid], reply=['blob'], environ={'HTTP_HOST': 'localhost'})) def test_Command_ReplyForGET(self): @@ -373,44 +534,46 @@ class RoutesTest(tests.Test): def prop(self, value): return value - @db.indexed_property(prefix='L', localized=True, default='') + @db.indexed_property(db.Localized, prefix='L', default={}) def localized_prop(self, value): return value - self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) - guid = self.call('POST', path=['testdocument'], content={'prop': 'value'}) + volume = db.Volume(tests.tmpdir, [TestDocument]) + router = Router(db.Routes(volume)) + guid = this.call(method='POST', path=['testdocument'], content={'prop': 'value'}) self.assertEqual( ['guid', 'prop'], - self.call('GET', path=['testdocument', guid], reply=['guid', 'prop']).keys()) + this.call(method='GET', path=['testdocument', guid], reply=['guid', 'prop']).keys()) self.assertEqual( ['guid'], - self.call('GET', path=['testdocument'])['result'][0].keys()) + this.call(method='GET', path=['testdocument'])['result'][0].keys()) self.assertEqual( sorted(['guid', 'prop']), - sorted(self.call('GET', path=['testdocument'], reply=['prop', 'guid'])['result'][0].keys())) + sorted(this.call(method='GET', path=['testdocument'], reply=['prop', 'guid'])['result'][0].keys())) self.assertEqual( sorted(['prop']), - sorted(self.call('GET', path=['testdocument'], reply=['prop'])['result'][0].keys())) + sorted(this.call(method='GET', path=['testdocument'], reply=['prop'])['result'][0].keys())) def test_DecodeBeforeSetting(self): class TestDocument(db.Resource): - @db.indexed_property(slot=1, typecast=int) + @db.indexed_property(db.Numeric, slot=1) def prop(self, value): return value - self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) + volume = db.Volume(tests.tmpdir, [TestDocument]) + router = Router(db.Routes(volume)) - guid = self.call('POST', path=['testdocument'], content={'prop': '-1'}) - self.assertEqual(-1, self.call('GET', path=['testdocument', guid, 'prop'])) + guid = this.call(method='POST', path=['testdocument'], content={'prop': '-1'}) + self.assertEqual(-1, this.call(method='GET', path=['testdocument', guid, 'prop'])) def test_LocalizedSet(self): - toolkit._default_langs = ['en'] + i18n._default_langs = ['en'] class TestDocument(db.Resource): @@ -418,32 +581,23 @@ class RoutesTest(tests.Test): def prop(self, value): return value - @db.indexed_property(prefix='L', localized=True, default='') + @db.indexed_property(db.Localized, prefix='L', default={}) def localized_prop(self, value): return value - self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) - 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(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(localized_prop='value_raw2')[0]]) + volume = db.Volume(tests.tmpdir, [TestDocument]) + router = Router(db.Routes(volume)) + directory = volume['testdocument'] + guid = this.call(method='POST', path=['testdocument'], content={'localized_prop': 'value_ru'}, + environ={'HTTP_ACCEPT_LANGUAGE': 'ru'}) - guid = self.call('POST', path=['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(localized_prop='value_ru')[0]]) - self.call('PUT', path=['testdocument', guid], accept_language=['en'], content={'localized_prop': 'value_en'}) + this.call(method='PUT', path=['testdocument', guid], content={'localized_prop': 'value_en'}, + environ={'HTTP_ACCEPT_LANGUAGE': 'en'}) self.assertEqual({'ru': 'value_ru', 'en': 'value_en'}, directory.get(guid)['localized_prop']) self.assertEqual( [guid], @@ -460,14 +614,15 @@ class RoutesTest(tests.Test): def prop(self, value): return value - @db.indexed_property(prefix='L', localized=True, default='') + @db.indexed_property(db.Localized, prefix='L', default={}) def localized_prop(self, value): return value - self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) - directory = self.volume['testdocument'] + volume = db.Volume(tests.tmpdir, [TestDocument]) + router = Router(db.Routes(volume)) + directory = volume['testdocument'] - guid = self.call('POST', path=['testdocument'], content={ + guid = this.call(method='POST', path=['testdocument'], content={ 'localized_prop': { 'ru': 'value_ru', 'es': 'value_es', @@ -475,63 +630,78 @@ class RoutesTest(tests.Test): }, }) - toolkit._default_langs = ['en'] + i18n._default_langs = ['en'] self.assertEqual( {'localized_prop': 'value_en'}, - self.call('GET', path=['testdocument', guid], reply=['localized_prop'])) + this.call(method='GET', path=['testdocument', guid], reply=['localized_prop'])) self.assertEqual( {'localized_prop': 'value_ru'}, - self.call('GET', path=['testdocument', guid], accept_language=['ru'], reply=['localized_prop'])) + this.call(method='GET', path=['testdocument', guid], reply=['localized_prop'], + environ={'HTTP_ACCEPT_LANGUAGE': 'ru'})) self.assertEqual( 'value_ru', - self.call('GET', path=['testdocument', guid, 'localized_prop'], accept_language=['ru', 'es'])) + this.call(method='GET', path=['testdocument', guid, 'localized_prop'], + environ={'HTTP_ACCEPT_LANGUAGE': 'ru,es'})) self.assertEqual( [{'localized_prop': 'value_ru'}], - self.call('GET', path=['testdocument'], accept_language=['foo', 'ru', 'es'], reply=['localized_prop'])['result']) + this.call(method='GET', path=['testdocument'], reply=['localized_prop'], + environ={'HTTP_ACCEPT_LANGUAGE': 'foo,ru,es'})['result']) self.assertEqual( {'localized_prop': 'value_ru'}, - self.call('GET', path=['testdocument', guid], accept_language=['ru-RU'], reply=['localized_prop'])) + this.call(method='GET', path=['testdocument', guid], reply=['localized_prop'], + environ={'HTTP_ACCEPT_LANGUAGE': 'ru-RU'})) self.assertEqual( 'value_ru', - self.call('GET', path=['testdocument', guid, 'localized_prop'], accept_language=['ru-RU', 'es'])) + this.call(method='GET', path=['testdocument', guid, 'localized_prop'], + environ={'HTTP_ACCEPT_LANGUAGE': 'ru-RU,es'})) self.assertEqual( [{'localized_prop': 'value_ru'}], - self.call('GET', path=['testdocument'], accept_language=['foo', 'ru-RU', 'es'], reply=['localized_prop'])['result']) + this.call(method='GET', path=['testdocument'], reply=['localized_prop'], + environ={'HTTP_ACCEPT_LANGUAGE': 'ru-RU,es'})['result']) self.assertEqual( {'localized_prop': 'value_es'}, - self.call('GET', path=['testdocument', guid], accept_language=['es'], reply=['localized_prop'])) + this.call(method='GET', path=['testdocument', guid], reply=['localized_prop'], + environ={'HTTP_ACCEPT_LANGUAGE': 'es'})) self.assertEqual( 'value_es', - self.call('GET', path=['testdocument', guid, 'localized_prop'], accept_language=['es', 'ru'])) + this.call(method='GET', path=['testdocument', guid, 'localized_prop'], + environ={'HTTP_ACCEPT_LANGUAGE': 'es,ru'})) self.assertEqual( [{'localized_prop': 'value_es'}], - self.call('GET', path=['testdocument'], accept_language=['foo', 'es', 'ru'], reply=['localized_prop'])['result']) + this.call(method='GET', path=['testdocument'], reply=['localized_prop'], + environ={'HTTP_ACCEPT_LANGUAGE': 'foo,es,ru'})['result']) self.assertEqual( {'localized_prop': 'value_en'}, - self.call('GET', path=['testdocument', guid], accept_language=['fr'], reply=['localized_prop'])) + this.call(method='GET', path=['testdocument', guid], reply=['localized_prop'], + environ={'HTTP_ACCEPT_LANGUAGE': 'fr'})) self.assertEqual( 'value_en', - self.call('GET', path=['testdocument', guid, 'localized_prop'], accept_language=['fr', 'za'])) + this.call(method='GET', path=['testdocument', guid, 'localized_prop'], + environ={'HTTP_ACCEPT_LANGUAGE': 'fr,za'})) self.assertEqual( [{'localized_prop': 'value_en'}], - self.call('GET', path=['testdocument'], accept_language=['foo', 'fr', 'za'], reply=['localized_prop'])['result']) + this.call(method='GET', path=['testdocument'], reply=['localized_prop'], + environ={'HTTP_ACCEPT_LANGUAGE': 'foo,fr,za'})['result']) - toolkit._default_langs = ['foo'] + i18n._default_langs = ['foo'] fallback_lang = sorted(['ru', 'es', 'en'])[0] self.assertEqual( {'localized_prop': 'value_%s' % fallback_lang}, - self.call('GET', path=['testdocument', guid], accept_language=['fr'], reply=['localized_prop'])) + this.call(method='GET', path=['testdocument', guid], reply=['localized_prop'], + environ={'HTTP_ACCEPT_LANGUAGE': 'fr'})) self.assertEqual( 'value_%s' % fallback_lang, - self.call('GET', path=['testdocument', guid, 'localized_prop'], accept_language=['fr', 'za'])) + this.call(method='GET', path=['testdocument', guid, 'localized_prop'], + environ={'HTTP_ACCEPT_LANGUAGE': 'fr,za'})) self.assertEqual( [{'localized_prop': 'value_%s' % fallback_lang}], - self.call('GET', path=['testdocument'], accept_language=['foo', 'fr', 'za'], reply=['localized_prop'])['result']) + this.call(method='GET', path=['testdocument'], reply=['localized_prop'], + environ={'HTTP_ACCEPT_LANGUAGE': 'foo,fr,za'})['result']) def test_OpenByModuleName(self): self.touch( @@ -543,9 +713,9 @@ class RoutesTest(tests.Test): ) sys.path.insert(0, '.') - volume = db.Volume('.', ['foo.bar'], lambda event: None) - assert exists('bar/index') + volume = db.Volume('.', ['foo.bar']) volume['bar'].find() + assert exists('bar/index') volume.close() def test_Command_GetBlobSetByUrl(self): @@ -556,21 +726,25 @@ class RoutesTest(tests.Test): def prop(self, value): return value - @db.blob_property() + @db.stored_property(db.Blob) def blob(self, value): return value - @db.indexed_property(prefix='L', localized=True, default='') + @db.indexed_property(db.Localized, prefix='L', default={}) def localized_prop(self, value): return value - self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) - guid = self.call('POST', path=['testdocument'], content={}) - self.call('PUT', path=['testdocument', guid, 'blob'], url='http://sugarlabs.org') + volume = db.Volume(tests.tmpdir, [TestDocument]) + router = Router(db.Routes(volume)) + guid = this.call(method='POST', path=['testdocument'], content={}) + this.call(method='PUT', path=['testdocument', guid, 'blob'], content={ + 'digest': 'digest', + 'url': 'http://sugarlabs.org', + }, content_type='application/json') self.assertEqual( 'http://sugarlabs.org', - self.call('GET', path=['testdocument', guid, 'blob'])['url']) + this.call(method='GET', path=['testdocument', guid, 'blob'])['url']) def test_on_create(self): @@ -580,24 +754,25 @@ class RoutesTest(tests.Test): def prop(self, value): return value - @db.indexed_property(prefix='L', localized=True, default='') + @db.indexed_property(db.Localized, prefix='L', default={}) def localized_prop(self, value): return value - self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) + volume = db.Volume(tests.tmpdir, [TestDocument]) + router = Router(db.Routes(volume)) ts = int(time.time()) - guid = self.call('POST', path=['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) + guid = this.call(method='POST', path=['testdocument'], content={}) + assert volume['testdocument'].get(guid)['ctime'] in range(ts - 1, ts + 1) + assert volume['testdocument'].get(guid)['mtime'] in range(ts - 1, ts + 1) def test_on_create_Override(self): class Routes(db.Routes): - def on_create(self, request, props, event): + def on_create(self, request, props): props['prop'] = 'overriden' - db.Routes.on_create(self, request, props, event) + db.Routes.on_create(self, request, props) class TestDocument(db.Resource): @@ -605,17 +780,18 @@ class RoutesTest(tests.Test): def prop(self, value): return value - @db.indexed_property(prefix='L', localized=True, default='') + @db.indexed_property(db.Localized, prefix='L', default={}) def localized_prop(self, value): return value - self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) + volume = db.Volume(tests.tmpdir, [TestDocument]) + router = Router(Routes(volume)) - guid = self.call('POST', ['testdocument'], content={'prop': 'foo'}, routes=Routes) - self.assertEqual('overriden', self.volume['testdocument'].get(guid)['prop']) + guid = this.call(method='POST', path=['testdocument'], content={'prop': 'foo'}, routes=Routes) + self.assertEqual('overriden', volume['testdocument'].get(guid)['prop']) - self.call('PUT', ['testdocument', guid], content={'prop': 'bar'}, routes=Routes) - self.assertEqual('bar', self.volume['testdocument'].get(guid)['prop']) + this.call(method='PUT', path=['testdocument', guid], content={'prop': 'bar'}, routes=Routes) + self.assertEqual('bar', volume['testdocument'].get(guid)['prop']) def test_on_update(self): @@ -625,26 +801,28 @@ class RoutesTest(tests.Test): def prop(self, value): return value - @db.indexed_property(prefix='L', localized=True, default='') + @db.indexed_property(db.Localized, prefix='L', default={}) def localized_prop(self, value): return value - self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) - guid = self.call('POST', path=['testdocument'], content={}) - prev_mtime = self.volume['testdocument'].get(guid)['mtime'] + volume = db.Volume(tests.tmpdir, [TestDocument]) + router = Router(db.Routes(volume)) + + guid = this.call(method='POST', path=['testdocument'], content={}) + prev_mtime = volume['testdocument'].get(guid)['mtime'] time.sleep(1) - self.call('PUT', path=['testdocument', guid], content={'prop': 'probe'}) - assert self.volume['testdocument'].get(guid)['mtime'] - prev_mtime >= 1 + this.call(method='PUT', path=['testdocument', guid], content={'prop': 'probe'}) + assert volume['testdocument'].get(guid)['mtime'] - prev_mtime >= 1 def test_on_update_Override(self): class Routes(db.Routes): - def on_update(self, request, props, event): + def on_update(self, request, props): props['prop'] = 'overriden' - db.Routes.on_update(self, request, props, event) + db.Routes.on_update(self, request, props) class TestDocument(db.Resource): @@ -652,17 +830,18 @@ class RoutesTest(tests.Test): def prop(self, value): return value - @db.indexed_property(prefix='L', localized=True, default='') + @db.indexed_property(db.Localized, prefix='L', default={}) def localized_prop(self, value): return value - self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) + volume = db.Volume(tests.tmpdir, [TestDocument]) + router = Router(Routes(volume)) - guid = self.call('POST', ['testdocument'], content={'prop': 'foo'}, routes=Routes) - self.assertEqual('foo', self.volume['testdocument'].get(guid)['prop']) + guid = this.call(method='POST', path=['testdocument'], content={'prop': 'foo'}, routes=Routes) + self.assertEqual('foo', volume['testdocument'].get(guid)['prop']) - self.call('PUT', ['testdocument', guid], content={'prop': 'bar'}, routes=Routes) - self.assertEqual('overriden', self.volume['testdocument'].get(guid)['prop']) + this.call(method='PUT', path=['testdocument', guid], content={'prop': 'bar'}, routes=Routes) + self.assertEqual('overriden', volume['testdocument'].get(guid)['prop']) def __test_DoNotPassGuidsForCreate(self): @@ -672,13 +851,15 @@ class RoutesTest(tests.Test): def prop(self, value): return value - @db.indexed_property(prefix='L', localized=True, default='') + @db.indexed_property(db.Localized, prefix='L', default={}) def localized_prop(self, value): return value - self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) - self.assertRaises(http.Forbidden, self.call, 'POST', path=['testdocument'], content={'guid': 'foo'}) - guid = self.call('POST', path=['testdocument'], content={}) + volume = db.Volume(tests.tmpdir, [TestDocument]) + router = Router(db.Routes(volume)) + + self.assertRaises(http.Forbidden, this.call, method='POST', path=['testdocument'], content={'guid': 'foo'}) + guid = this.call(method='POST', path=['testdocument'], content={}) assert guid def test_seqno(self): @@ -689,7 +870,8 @@ class RoutesTest(tests.Test): class Document2(db.Resource): pass - volume = db.Volume(tests.tmpdir, [Document1, Document2], lambda event: None) + volume = db.Volume(tests.tmpdir, [Document1, Document2]) + router = Router(db.Routes(volume)) assert not exists('seqno') self.assertEqual(0, volume.seqno.value) @@ -706,8 +888,8 @@ class RoutesTest(tests.Test): self.assertEqual(4, volume.seqno.value) assert not exists('seqno') volume.seqno.commit() - assert exists('seqno') - volume = db.Volume(tests.tmpdir, [Document1, Document2], lambda event: None) + assert exists('db.seqno') + volume = db.Volume(tests.tmpdir, [Document1, Document2]) self.assertEqual(4, volume.seqno.value) def test_Events(self): @@ -726,7 +908,7 @@ class RoutesTest(tests.Test): def prop(self, value): pass - @db.blob_property() + @db.stored_property(db.Blob) def blob(self, value): return value @@ -739,13 +921,15 @@ class RoutesTest(tests.Test): ) events = [] - volume = db.Volume(tests.tmpdir, [Document1, Document2], lambda event: events.append(event)) + this.broadcast = lambda x: events.append(x) + volume = db.Volume(tests.tmpdir, [Document1, Document2]) + volume['document1'] + volume['document2'] coroutine.sleep(.1) mtime = int(os.stat('document1/index/mtime').st_mtime) self.assertEqual([ {'event': 'commit', 'resource': 'document1', 'mtime': mtime}, - {'event': 'populate', 'resource': 'document1', 'mtime': mtime}, ], events) del events[:] @@ -794,43 +978,45 @@ class RoutesTest(tests.Test): def prop(self, value): pass - @db.blob_property(acl=ACL.READ) + @db.stored_property(db.Blob, acl=ACL.READ) def blob(self, value): return value - self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) - guid = self.call('POST', path=['testdocument'], content={}) + volume = db.Volume(tests.tmpdir, [TestDocument]) + router = Router(db.Routes(volume)) + guid = this.call(method='POST', path=['testdocument'], content={}) - self.assertRaises(http.Forbidden, self.call, 'POST', path=['testdocument'], content={'prop': 'value'}) - self.assertRaises(http.Forbidden, self.call, 'PUT', path=['testdocument', guid], content={'prop': 'value'}) - self.assertRaises(http.Forbidden, self.call, 'PUT', path=['testdocument', guid], content={'blob': 'value'}) - self.assertRaises(http.Forbidden, self.call, 'PUT', path=['testdocument', guid, 'prop'], content='value') - self.assertRaises(http.Forbidden, self.call, 'PUT', path=['testdocument', guid, 'blob'], content='value') + self.assertRaises(http.Forbidden, this.call, method='POST', path=['testdocument'], content={'prop': 'value'}) + self.assertRaises(http.Forbidden, this.call, method='PUT', path=['testdocument', guid], content={'prop': 'value'}) + self.assertRaises(http.Forbidden, this.call, method='PUT', path=['testdocument', guid], content={'blob': 'value'}) + self.assertRaises(http.Forbidden, this.call, method='PUT', path=['testdocument', guid, 'prop'], content='value') + self.assertRaises(http.Forbidden, this.call, method='PUT', path=['testdocument', guid, 'blob'], content='value') def test_BlobsWritePermissions(self): class TestDocument(db.Resource): - @db.blob_property(acl=ACL.CREATE | ACL.WRITE) + @db.stored_property(db.Blob, acl=ACL.CREATE | ACL.WRITE) def blob1(self, value): return value - @db.blob_property(acl=ACL.CREATE) + @db.stored_property(db.Blob, acl=ACL.CREATE) def blob2(self, value): return value - self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) + volume = db.Volume(tests.tmpdir, [TestDocument]) + router = Router(db.Routes(volume)) - guid = self.call('POST', path=['testdocument'], content={}) - self.call('PUT', path=['testdocument', guid], content={'blob1': 'value1', 'blob2': 'value2'}) - self.call('PUT', path=['testdocument', guid], content={'blob1': 'value1'}) - self.assertRaises(http.Forbidden, self.call, 'PUT', path=['testdocument', guid], content={'blob2': 'value2_'}) + guid = this.call(method='POST', path=['testdocument'], content={}) + this.call(method='PUT', path=['testdocument', guid], content={'blob1': 'value1', 'blob2': 'value2'}) + this.call(method='PUT', path=['testdocument', guid], content={'blob1': 'value1'}) + self.assertRaises(http.Forbidden, this.call, method='PUT', path=['testdocument', guid], content={'blob2': 'value2_'}) - guid = self.call('POST', path=['testdocument'], content={}) - self.call('PUT', path=['testdocument', guid, 'blob1'], content='value1') - self.call('PUT', path=['testdocument', guid, 'blob2'], content='value2') - self.call('PUT', path=['testdocument', guid, 'blob1'], content='value1_') - self.assertRaises(http.Forbidden, self.call, 'PUT', path=['testdocument', guid, 'blob2'], content='value2_') + guid = this.call(method='POST', path=['testdocument'], content={}) + this.call(method='PUT', path=['testdocument', guid, 'blob1'], content='value1') + this.call(method='PUT', path=['testdocument', guid, 'blob2'], content='value2') + this.call(method='PUT', path=['testdocument', guid, 'blob1'], content='value1_') + self.assertRaises(http.Forbidden, this.call, method='PUT', path=['testdocument', guid, 'blob2'], content='value2_') def test_properties_OverrideGet(self): @@ -844,30 +1030,32 @@ class RoutesTest(tests.Test): def prop2(self, value): return -1 - @db.blob_property() + @db.stored_property(db.Blob) def blob(self, meta): meta['blob'] = 'new-blob' return meta - self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) - guid = self.call('POST', path=['testdocument'], content={}) + volume = db.Volume(tests.tmpdir, [TestDocument]) + router = Router(db.Routes(volume)) + + guid = this.call(method='POST', path=['testdocument'], content={}) self.touch(('new-blob', 'new-blob')) - self.call('PUT', path=['testdocument', guid, 'blob'], content='old-blob') + this.call(method='PUT', path=['testdocument', guid, 'blob'], content='old-blob') self.assertEqual( 'new-blob', - self.call('GET', path=['testdocument', guid, 'blob'])['blob']) + this.call(method='GET', path=['testdocument', guid, 'blob'])['blob']) self.assertEqual( '1', - self.call('GET', path=['testdocument', guid, 'prop1'])) + this.call(method='GET', path=['testdocument', guid, 'prop1'])) self.assertEqual( -1, - self.call('GET', path=['testdocument', guid, 'prop2'])) + this.call(method='GET', path=['testdocument', guid, 'prop2'])) self.assertEqual( {'prop1': '1', 'prop2': -1}, - self.call('GET', path=['testdocument', guid], reply=['prop1', 'prop2'])) + this.call(method='GET', path=['testdocument', guid], reply=['prop1', 'prop2'])) - def test_properties_OverrideSet(self): + def test_properties_OverrideSetter(self): class TestDocument(db.Resource): @@ -879,53 +1067,47 @@ class RoutesTest(tests.Test): def prop(self, value): return '_%s' % value - @db.blob_property() - def blob1(self, meta): - return meta + volume = db.Volume(tests.tmpdir, [TestDocument]) + router = Router(db.Routes(volume)) + guid = this.call(method='POST', path=['testdocument'], content={}) - @blob1.setter - def blob1(self, value): - return Blob({'url': file(value['blob']).read()}) + self.assertEqual('_1', this.call(method='GET', path=['testdocument', guid, 'prop'])) - @db.blob_property() - def blob2(self, meta): - return meta + this.call(method='PUT', path=['testdocument', guid, 'prop'], content='2') + self.assertEqual('_2', this.call(method='GET', path=['testdocument', guid, 'prop'])) - @blob2.setter - def blob2(self, value): - with toolkit.NamedTemporaryFile(delete=False) as f: - f.write(' %s ' % file(value['blob']).read()) - value['blob'] = f.name - return value + this.call(method='PUT', path=['testdocument', guid], content={'prop': 3}) + self.assertEqual('_3', this.call(method='GET', path=['testdocument', guid, 'prop'])) - self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) - guid = self.call('POST', path=['testdocument'], content={}) + def test_properties_AccessToOldValuesInSetters(self): + + class TestDocument(db.Resource): - self.assertEqual('_1', self.call('GET', path=['testdocument', guid, 'prop'])) - self.assertRaises(http.NotFound, self.call, 'GET', path=['testdocument', guid, 'blob1']) + @db.stored_property(db.Numeric) + def prop(self, value): + return value - self.call('PUT', path=['testdocument', guid, 'prop'], content='2') - self.assertEqual('_2', self.call('GET', path=['testdocument', guid, 'prop'])) - self.assertRaises(http.NotFound, self.call, 'GET', path=['testdocument', guid, 'blob1']) + @prop.setter + def prop(self, value): + return value + (self['prop'] or 0) - self.call('PUT', path=['testdocument', guid], content={'prop': 3}) - self.assertEqual('_3', self.call('GET', path=['testdocument', guid, 'prop'])) - self.assertRaises(http.NotFound, self.call, 'GET', path=['testdocument', guid, 'blob1']) + volume = db.Volume(tests.tmpdir, [TestDocument]) + router = Router(db.Routes(volume)) - self.call('PUT', path=['testdocument', guid, 'blob1'], content='blob_url') - self.assertEqual('blob_url', self.call('GET', path=['testdocument', guid, 'blob1'])['url']) + guid = this.call(method='POST', path=['testdocument'], content={'prop': 1}) + self.assertEqual(1, this.call(method='GET', path=['testdocument', guid, 'prop'])) - guid = self.call('POST', path=['testdocument'], content={'blob2': 'foo'}) - self.assertEqual(' foo ', file(self.call('GET', path=['testdocument', guid, 'blob2'])['blob']).read()) + this.call(method='PUT', path=['testdocument', guid, 'prop'], content='2') + self.assertEqual(3, this.call(method='GET', path=['testdocument', guid, 'prop'])) - self.call('PUT', path=['testdocument', guid, 'blob2'], content='bar') - self.assertEqual(' bar ', file(self.call('GET', path=['testdocument', guid, 'blob2'])['blob']).read()) + this.call(method='PUT', path=['testdocument', guid], content={'prop': 3}) + self.assertEqual(6, this.call(method='GET', path=['testdocument', guid, 'prop'])) def test_properties_CallSettersAtTheEnd(self): class TestDocument(db.Resource): - @db.indexed_property(slot=1, typecast=int) + @db.indexed_property(db.Numeric, slot=1) def prop1(self, value): return value @@ -933,7 +1115,7 @@ class RoutesTest(tests.Test): def prop1(self, value): return self['prop3'] + value - @db.indexed_property(slot=2, typecast=int) + @db.indexed_property(db.Numeric, slot=2) def prop2(self, value): return value @@ -941,107 +1123,40 @@ class RoutesTest(tests.Test): def prop2(self, value): return self['prop3'] - value - @db.indexed_property(slot=3, typecast=int) + @db.indexed_property(db.Numeric, slot=3) def prop3(self, value): return value - self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) - guid = self.call('POST', path=['testdocument'], content={'prop1': 1, 'prop2': 2, 'prop3': 3}) - self.assertEqual(4, self.call('GET', path=['testdocument', guid, 'prop1'])) - self.assertEqual(1, self.call('GET', path=['testdocument', guid, 'prop2'])) + volume = db.Volume(tests.tmpdir, [TestDocument]) + router = Router(db.Routes(volume)) + + guid = this.call(method='POST', path=['testdocument'], content={'prop1': 1, 'prop2': 2, 'prop3': 3}) + self.assertEqual(4, this.call(method='GET', path=['testdocument', guid, 'prop1'])) + self.assertEqual(1, this.call(method='GET', path=['testdocument', guid, 'prop2'])) def test_properties_PopulateRequiredPropsInSetters(self): class TestDocument(db.Resource): - @db.indexed_property(slot=1, typecast=int) + @db.indexed_property(db.Numeric, slot=1) def prop1(self, value): return value @prop1.setter def prop1(self, value): - self['prop2'] = value + 1 + self.post('prop2', value + 1) return value - @db.indexed_property(slot=2, typecast=int) + @db.indexed_property(db.Numeric, slot=2) def prop2(self, value): return value - @db.blob_property() - def prop3(self, value): - return value - - @prop3.setter - def prop3(self, value): - self['prop1'] = -1 - self['prop2'] = -2 - return value - - self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) - guid = self.call('POST', path=['testdocument'], content={'prop1': 1}) - self.assertEqual(1, self.call('GET', path=['testdocument', guid, 'prop1'])) - self.assertEqual(2, self.call('GET', path=['testdocument', guid, 'prop2'])) - - def test_properties_PopulateRequiredPropsInBlobSetter(self): - - class TestDocument(db.Resource): - - @db.blob_property() - def blob(self, value): - return value - - @blob.setter - def blob(self, value): - self['prop1'] = 1 - self['prop2'] = 2 - return value - - @db.indexed_property(slot=1, typecast=int) - def prop1(self, value): - return value - - @db.indexed_property(slot=2, typecast=int) - def prop2(self, value): - return value - - self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) - guid = self.call('POST', path=['testdocument'], content={'blob': ''}) - self.assertEqual(1, self.call('GET', path=['testdocument', guid, 'prop1'])) - self.assertEqual(2, self.call('GET', path=['testdocument', guid, 'prop2'])) - - def __test_SubCall(self): - - class TestDocument(db.Resource): - - @db.blob_property(mime_type='application/json') - def blob(self, value): - return value - - @blob.setter - def blob(self, value): - blob = file(value['blob']).read() - if '!' not in blob: - meta = self.meta('blob') - if meta: - blob = file(meta['blob']).read() + blob - with toolkit.NamedTemporaryFile(delete=False) as f: - f.write(blob) - value['blob'] = f.name - coroutine.spawn(self.post, blob) - return value - - def post(self, value): - self.request.call('PUT', path=['testdocument', self.guid, 'blob'], content=value + '!') + volume = db.Volume(tests.tmpdir, [TestDocument]) + router = Router(db.Routes(volume)) - self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) - - guid = self.call('POST', path=['testdocument'], content={'blob': '0'}) - coroutine.dispatch() - self.assertEqual('0!', file(self.call('GET', path=['testdocument', guid, 'blob'])['blob']).read()) - - self.call('PUT', path=['testdocument', guid, 'blob'], content='1') - coroutine.dispatch() - self.assertEqual('0!1!', file(self.call('GET', path=['testdocument', guid, 'blob'])['blob']).read()) + guid = this.call(method='POST', path=['testdocument'], content={'prop1': 1}) + self.assertEqual(1, this.call(method='GET', path=['testdocument', guid, 'prop1'])) + self.assertEqual(2, this.call(method='GET', path=['testdocument', guid, 'prop2'])) def test_Group(self): @@ -1051,15 +1166,16 @@ class RoutesTest(tests.Test): def prop(self, value): return value - self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) + volume = db.Volume(tests.tmpdir, [TestDocument]) + router = Router(db.Routes(volume)) - self.call('POST', path=['testdocument'], content={'prop': 1}) - self.call('POST', path=['testdocument'], content={'prop': 2}) - self.call('POST', path=['testdocument'], content={'prop': 1}) + this.call(method='POST', path=['testdocument'], content={'prop': 1}) + this.call(method='POST', path=['testdocument'], content={'prop': 2}) + this.call(method='POST', path=['testdocument'], content={'prop': 1}) self.assertEqual( sorted([{'prop': 1}, {'prop': 2}]), - sorted(self.call('GET', path=['testdocument'], reply='prop', group_by='prop')['result'])) + sorted(this.call(method='GET', path=['testdocument'], reply='prop', group_by='prop')['result'])) def test_CallSetterEvenIfThereIsNoCreatePermissions(self): @@ -1073,12 +1189,13 @@ class RoutesTest(tests.Test): def prop(self, value): return value + 1 - self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) + volume = db.Volume(tests.tmpdir, [TestDocument]) + router = Router(db.Routes(volume)) - self.assertRaises(http.Forbidden, self.call, 'POST', path=['testdocument'], content={'prop': 1}) + self.assertRaises(http.Forbidden, this.call, method='POST', path=['testdocument'], content={'prop': 1}) - guid = self.call('POST', path=['testdocument'], content={}) - self.assertEqual(1, self.call('GET', path=['testdocument', guid, 'prop'])) + guid = this.call(method='POST', path=['testdocument'], content={}) + self.assertEqual(1, this.call(method='GET', path=['testdocument', guid, 'prop'])) def test_ReturnDefualtsForMissedProps(self): @@ -1088,57 +1205,34 @@ class RoutesTest(tests.Test): def prop(self, value): return value - self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) - guid = self.call('POST', path=['testdocument'], content={'prop': 'set'}) + volume = db.Volume(tests.tmpdir, [TestDocument]) + router = Router(db.Routes(volume)) + guid = this.call(method='POST', path=['testdocument'], content={'prop': 'set'}) self.assertEqual( [{'prop': 'set'}], - self.call('GET', path=['testdocument'], reply='prop')['result']) + this.call(method='GET', path=['testdocument'], reply='prop')['result']) self.assertEqual( {'prop': 'set'}, - self.call('GET', path=['testdocument', guid], reply='prop')) + this.call(method='GET', path=['testdocument', guid], reply='prop')) self.assertEqual( 'set', - self.call('GET', path=['testdocument', guid, 'prop'])) + this.call(method='GET', path=['testdocument', guid, 'prop'])) os.unlink('testdocument/%s/%s/prop' % (guid[:2], guid)) self.assertEqual( [{'prop': 'default'}], - self.call('GET', path=['testdocument'], reply='prop')['result']) + this.call(method='GET', path=['testdocument'], reply='prop')['result']) self.assertEqual( {'prop': 'default'}, - self.call('GET', path=['testdocument', guid], reply='prop')) + this.call(method='GET', path=['testdocument', guid], reply='prop')) self.assertEqual( 'default', - self.call('GET', path=['testdocument', guid, 'prop'])) - - def test_PopulateNonDefualtPropsInSetters(self): - - class TestDocument(db.Resource): - - @db.indexed_property(slot=1) - def prop1(self, value): - return value - - @db.indexed_property(slot=2, default='default') - def prop2(self, value): - return all - - @prop2.setter - def prop2(self, value): - if value != 'default': - self['prop1'] = value - return value - - self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) - - self.assertRaises(RuntimeError, self.call, 'POST', path=['testdocument'], content={}) - - guid = self.call('POST', path=['testdocument'], content={'prop2': 'value2'}) - self.assertEqual('value2', self.call('GET', path=['testdocument', guid, 'prop1'])) + this.call(method='GET', path=['testdocument', guid, 'prop'])) def test_prop_meta(self): + files.update('url', {'url': 'http://new', 'foo': 'bar', 'size': 100}) class TestDocument(db.Resource): @@ -1146,44 +1240,55 @@ class RoutesTest(tests.Test): def prop(self, value): return value - @db.blob_property() + @db.stored_property(db.Blob) def blob1(self, value): return value - @db.blob_property() + @db.stored_property(db.Blob) def blob2(self, value): return value @blob2.setter def blob2(self, value): - return {'url': 'http://new', 'foo': 'bar', 'blob_size': 100} + return 'url' - self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) - guid = self.call('POST', ['testdocument'], content = {'prop': 'prop', 'blob1': 'blob', 'blob2': ''}) + volume = db.Volume(tests.tmpdir, [TestDocument]) + router = Router(db.Routes(volume)) + guid = this.call(method='POST', path=['testdocument'], content = {'prop': 'prop', 'blob1': 'blob', 'blob2': ''}) - assert self.call('HEAD', ['testdocument', guid, 'prop']) is None - meta = self.volume['testdocument'].get(guid).meta('prop') + response = Response() + assert this.call(response=response, + method='HEAD', path=['testdocument', guid, 'prop']) is None + meta = volume['testdocument'].get(guid).meta('prop') meta.pop('value') - self.assertEqual(meta, self.response.meta) - self.assertEqual(formatdate(meta['mtime'], localtime=False, usegmt=True), self.response.last_modified) - - assert self.call('HEAD', ['testdocument', guid, 'blob1'], host='localhost') is None - meta = self.volume['testdocument'].get(guid).meta('blob1') - meta.pop('blob') - self.assertEqual(meta, self.response.meta) - self.assertEqual(len('blob'), self.response.content_length) - self.assertEqual(formatdate(meta['mtime'], localtime=False, usegmt=True), self.response.last_modified) - - assert self.call('HEAD', ['testdocument', guid, 'blob2']) is None - meta = self.volume['testdocument'].get(guid).meta('blob2') - self.assertEqual(meta, self.response.meta) - self.assertEqual(100, self.response.content_length) - self.assertEqual(formatdate(meta['mtime'], localtime=False, usegmt=True), self.response.last_modified) - - assert self.call('GET', ['testdocument', guid, 'blob2']) is not None - meta = self.volume['testdocument'].get(guid).meta('blob2') - self.assertEqual(meta, self.response.meta) - self.assertEqual(formatdate(meta['mtime'], localtime=False, usegmt=True), self.response.last_modified) + self.assertEqual(meta, response.meta) + self.assertEqual(formatdate(meta['mtime'], localtime=False, usegmt=True), response.last_modified) + + response = Response() + assert this.call(response=response, + method='HEAD', path=['testdocument', guid, 'blob1'], environ={'HTTP_HOST': 'localhost'}) is None + meta = volume['testdocument'].get(guid).meta('blob1') + meta.pop('value') + self.assertEqual(meta, response.meta) + self.assertEqual(len('blob'), response.content_length) + self.assertEqual(formatdate(meta['mtime'], localtime=False, usegmt=True), response.last_modified) + + response = Response() + assert this.call(response=response, + method='HEAD', path=['testdocument', guid, 'blob2']) is None + meta = volume['testdocument'].get(guid).meta('blob2') + meta.pop('value') + self.assertEqual(meta, response.meta) + self.assertEqual(100, response.content_length) + self.assertEqual(formatdate(meta['mtime'], localtime=False, usegmt=True), response.last_modified) + + response = Response() + assert this.call(response=response, + method='GET', path=['testdocument', guid, 'blob2']) is not None + meta = volume['testdocument'].get(guid).meta('blob2') + meta.pop('value') + self.assertEqual(meta, response.meta) + self.assertEqual(formatdate(meta['mtime'], localtime=False, usegmt=True), response.last_modified) def test_DefaultAuthor(self): @@ -1196,25 +1301,26 @@ class RoutesTest(tests.Test): class Document(db.Resource): pass - self.volume = db.Volume('db', [User, Document]) + volume = db.Volume(tests.tmpdir, [User, Document]) + router = Router(db.Routes(volume)) - guid = self.call('POST', ['document'], content={}, principal='user') + guid = this.call(method='POST', path=['document'], content={}, principal='user') self.assertEqual( [{'name': 'user', 'role': 2}], - self.call('GET', ['document', guid, 'author'])) + this.call(method='GET', path=['document', guid, 'author'])) self.assertEqual( {'user': {'role': 2, 'order': 0}}, - self.volume['document'].get(guid)['author']) + volume['document'].get(guid)['author']) - self.volume['user'].create({'guid': 'user', 'pubkey': '', 'name': 'User'}) + volume['user'].create({'guid': 'user', 'pubkey': '', 'name': 'User'}) - guid = self.call('POST', ['document'], content={}, principal='user') + guid = this.call(method='POST', path=['document'], content={}, principal='user') self.assertEqual( [{'guid': 'user', 'name': 'User', 'role': 3}], - self.call('GET', ['document', guid, 'author'])) + this.call(method='GET', path=['document', guid, 'author'])) self.assertEqual( {'user': {'name': 'User', 'role': 3, 'order': 0}}, - self.volume['document'].get(guid)['author']) + volume['document'].get(guid)['author']) def test_FindByAuthor(self): @@ -1227,36 +1333,37 @@ class RoutesTest(tests.Test): class Document(db.Resource): pass - self.volume = db.Volume('db', [User, Document]) + volume = db.Volume(tests.tmpdir, [User, Document]) + router = Router(db.Routes(volume)) - self.volume['user'].create({'guid': 'user1', 'pubkey': '', 'name': 'UserName1'}) - self.volume['user'].create({'guid': 'user2', 'pubkey': '', 'name': 'User Name2'}) - self.volume['user'].create({'guid': 'user3', 'pubkey': '', 'name': 'User Name 3'}) + volume['user'].create({'guid': 'user1', 'pubkey': '', 'name': 'UserName1'}) + volume['user'].create({'guid': 'user2', 'pubkey': '', 'name': 'User Name2'}) + volume['user'].create({'guid': 'user3', 'pubkey': '', 'name': 'User Name 3'}) - guid1 = self.call('POST', ['document'], content={}, principal='user1') - guid2 = self.call('POST', ['document'], content={}, principal='user2') - guid3 = self.call('POST', ['document'], content={}, principal='user3') + guid1 = this.call(method='POST', path=['document'], content={}, principal='user1') + guid2 = this.call(method='POST', path=['document'], content={}, principal='user2') + guid3 = this.call(method='POST', path=['document'], content={}, principal='user3') self.assertEqual(sorted([ {'guid': guid1}, ]), - self.call('GET', ['document'], author='UserName1')['result']) + this.call(method='GET', path=['document'], author='UserName1')['result']) self.assertEqual(sorted([ {'guid': guid1}, ]), - sorted(self.call('GET', ['document'], query='author:UserName')['result'])) + sorted(this.call(method='GET', path=['document'], query='author:UserName')['result'])) self.assertEqual(sorted([ {'guid': guid1}, {'guid': guid2}, {'guid': guid3}, ]), - sorted(self.call('GET', ['document'], query='author:User')['result'])) + sorted(this.call(method='GET', path=['document'], query='author:User')['result'])) self.assertEqual(sorted([ {'guid': guid2}, {'guid': guid3}, ]), - sorted(self.call('GET', ['document'], query='author:Name')['result'])) + sorted(this.call(method='GET', path=['document'], query='author:Name')['result'])) def test_PreserveAuthorsOrder(self): @@ -1269,99 +1376,77 @@ class RoutesTest(tests.Test): class Document(db.Resource): pass - self.volume = db.Volume('db', [User, Document]) + volume = db.Volume(tests.tmpdir, [User, Document]) + router = Router(db.Routes(volume)) - self.volume['user'].create({'guid': 'user1', 'pubkey': '', 'name': 'User1'}) - self.volume['user'].create({'guid': 'user2', 'pubkey': '', 'name': 'User2'}) - self.volume['user'].create({'guid': 'user3', 'pubkey': '', 'name': 'User3'}) + volume['user'].create({'guid': 'user1', 'pubkey': '', 'name': 'User1'}) + volume['user'].create({'guid': 'user2', 'pubkey': '', 'name': 'User2'}) + volume['user'].create({'guid': 'user3', 'pubkey': '', 'name': 'User3'}) - guid = self.call('POST', ['document'], content={}, principal='user1') - self.call('PUT', ['document', guid], cmd='useradd', user='user2', role=0) - self.call('PUT', ['document', guid], cmd='useradd', user='user3', role=0) + guid = this.call(method='POST', path=['document'], content={}, principal='user1') + this.call(method='PUT', path=['document', guid], cmd='useradd', user='user2', role=0) + this.call(method='PUT', path=['document', guid], cmd='useradd', user='user3', role=0) self.assertEqual([ {'guid': 'user1', 'name': 'User1', 'role': 3}, {'guid': 'user2', 'name': 'User2', 'role': 1}, {'guid': 'user3', 'name': 'User3', 'role': 1}, ], - self.call('GET', ['document', guid, 'author'])) + this.call(method='GET', path=['document', guid, 'author'])) self.assertEqual({ 'user1': {'name': 'User1', 'role': 3, 'order': 0}, 'user2': {'name': 'User2', 'role': 1, 'order': 1}, 'user3': {'name': 'User3', 'role': 1, 'order': 2}, }, - self.volume['document'].get(guid)['author']) + volume['document'].get(guid)['author']) - self.call('PUT', ['document', guid], cmd='userdel', user='user2', principal='user1') - self.call('PUT', ['document', guid], cmd='useradd', user='user2', role=0) + this.call(method='PUT', path=['document', guid], cmd='userdel', user='user2', principal='user1') + this.call(method='PUT', path=['document', guid], cmd='useradd', user='user2', role=0) self.assertEqual([ {'guid': 'user1', 'name': 'User1', 'role': 3}, {'guid': 'user3', 'name': 'User3', 'role': 1}, {'guid': 'user2', 'name': 'User2', 'role': 1}, ], - self.call('GET', ['document', guid, 'author'])) + this.call(method='GET', path=['document', guid, 'author'])) self.assertEqual({ 'user1': {'name': 'User1', 'role': 3, 'order': 0}, 'user3': {'name': 'User3', 'role': 1, 'order': 2}, 'user2': {'name': 'User2', 'role': 1, 'order': 3}, }, - self.volume['document'].get(guid)['author']) + volume['document'].get(guid)['author']) - self.call('PUT', ['document', guid], cmd='userdel', user='user2', principal='user1') - self.call('PUT', ['document', guid], cmd='useradd', user='user2', role=0) + this.call(method='PUT', path=['document', guid], cmd='userdel', user='user2', principal='user1') + this.call(method='PUT', path=['document', guid], cmd='useradd', user='user2', role=0) self.assertEqual([ {'guid': 'user1', 'name': 'User1', 'role': 3}, {'guid': 'user3', 'name': 'User3', 'role': 1}, {'guid': 'user2', 'name': 'User2', 'role': 1}, ], - self.call('GET', ['document', guid, 'author'])) + this.call(method='GET', path=['document', guid, 'author'])) self.assertEqual({ 'user1': {'name': 'User1', 'role': 3, 'order': 0}, 'user3': {'name': 'User3', 'role': 1, 'order': 2}, 'user2': {'name': 'User2', 'role': 1, 'order': 3}, }, - self.volume['document'].get(guid)['author']) + volume['document'].get(guid)['author']) - self.call('PUT', ['document', guid], cmd='userdel', user='user3', principal='user1') - self.call('PUT', ['document', guid], cmd='useradd', user='user3', role=0) + this.call(method='PUT', path=['document', guid], cmd='userdel', user='user3', principal='user1') + this.call(method='PUT', path=['document', guid], cmd='useradd', user='user3', role=0) self.assertEqual([ {'guid': 'user1', 'name': 'User1', 'role': 3}, {'guid': 'user2', 'name': 'User2', 'role': 1}, {'guid': 'user3', 'name': 'User3', 'role': 1}, ], - self.call('GET', ['document', guid, 'author'])) + this.call(method='GET', path=['document', guid, 'author'])) self.assertEqual({ 'user1': {'name': 'User1', 'role': 3, 'order': 0}, 'user2': {'name': 'User2', 'role': 1, 'order': 3}, 'user3': {'name': 'User3', 'role': 1, 'order': 4}, }, - self.volume['document'].get(guid)['author']) - - def test_CopyAthors(self): - - class User(db.Resource): - - @db.indexed_property(slot=1) - def name(self, value): - return value - - class Document(db.Resource): - pass - - self.volume = db.Volume('db', [User, Document]) - self.volume['user'].create({'guid': 'user', 'pubkey': '', 'name': 'User'}) - - guid1 = self.call('POST', ['document'], content={}, principal='user') - self.assertEqual({'user': {'name': 'User', 'role': 3, 'order': 0}}, self.volume['document'].get(guid1)['author']) - author = self.call('GET', ['document', guid1, 'author']) - self.assertEqual([{'guid': 'user', 'role': 3, 'name': 'User'}], author) - - guid2 = self.volume['document'].create({'author': author}, setters=True) - author = self.call('GET', ['document', guid1, 'author']) - self.assertEqual({'user': {'name': 'User', 'role': 3, 'order': 0}}, self.volume['document'].get(guid2)['author']) + volume['document'].get(guid)['author']) def test_AddUser(self): @@ -1374,62 +1459,63 @@ class RoutesTest(tests.Test): class Document(db.Resource): pass - self.volume = db.Volume('db', [User, Document]) + volume = db.Volume(tests.tmpdir, [User, Document]) + router = Router(db.Routes(volume)) - self.volume['user'].create({'guid': 'user1', 'pubkey': '', 'name': 'User1'}) - self.volume['user'].create({'guid': 'user2', 'pubkey': '', 'name': 'User2'}) + volume['user'].create({'guid': 'user1', 'pubkey': '', 'name': 'User1'}) + volume['user'].create({'guid': 'user2', 'pubkey': '', 'name': 'User2'}) - guid = self.call('POST', ['document'], content={}, principal='user1') + guid = this.call(method='POST', path=['document'], content={}, principal='user1') self.assertEqual([ {'guid': 'user1', 'name': 'User1', 'role': 3}, ], - self.call('GET', ['document', guid, 'author'])) + this.call(method='GET', path=['document', guid, 'author'])) self.assertEqual({ 'user1': {'name': 'User1', 'role': 3, 'order': 0}, }, - self.volume['document'].get(guid)['author']) + volume['document'].get(guid)['author']) - self.call('PUT', ['document', guid], cmd='useradd', user='user2', role=2) + this.call(method='PUT', path=['document', guid], cmd='useradd', user='user2', role=2) self.assertEqual([ {'guid': 'user1', 'name': 'User1', 'role': 3}, {'guid': 'user2', 'name': 'User2', 'role': 3}, ], - self.call('GET', ['document', guid, 'author'])) + this.call(method='GET', path=['document', guid, 'author'])) self.assertEqual({ 'user1': {'name': 'User1', 'role': 3, 'order': 0}, 'user2': {'name': 'User2', 'role': 3, 'order': 1}, }, - self.volume['document'].get(guid)['author']) + volume['document'].get(guid)['author']) - self.call('PUT', ['document', guid], cmd='useradd', user='User3', role=3) + this.call(method='PUT', path=['document', guid], cmd='useradd', user='User3', role=3) self.assertEqual([ {'guid': 'user1', 'name': 'User1', 'role': 3}, {'guid': 'user2', 'name': 'User2', 'role': 3}, {'name': 'User3', 'role': 2}, ], - self.call('GET', ['document', guid, 'author'])) + this.call(method='GET', path=['document', guid, 'author'])) self.assertEqual({ 'user1': {'name': 'User1', 'role': 3, 'order': 0}, 'user2': {'name': 'User2', 'role': 3, 'order': 1}, 'User3': {'role': 2, 'order': 2}, }, - self.volume['document'].get(guid)['author']) + volume['document'].get(guid)['author']) - self.call('PUT', ['document', guid], cmd='useradd', user='User4', role=4) + this.call(method='PUT', path=['document', guid], cmd='useradd', user='User4', role=4) self.assertEqual([ {'guid': 'user1', 'name': 'User1', 'role': 3}, {'guid': 'user2', 'name': 'User2', 'role': 3}, {'name': 'User3', 'role': 2}, {'name': 'User4', 'role': 0}, ], - self.call('GET', ['document', guid, 'author'])) + this.call(method='GET', path=['document', guid, 'author'])) self.assertEqual({ 'user1': {'name': 'User1', 'role': 3, 'order': 0}, 'user2': {'name': 'User2', 'role': 3, 'order': 1}, 'User3': {'role': 2, 'order': 2}, 'User4': {'role': 0, 'order': 3}, }, - self.volume['document'].get(guid)['author']) + volume['document'].get(guid)['author']) def test_UpdateAuthor(self): @@ -1442,46 +1528,47 @@ class RoutesTest(tests.Test): class Document(db.Resource): pass - self.volume = db.Volume('db', [User, Document]) + volume = db.Volume(tests.tmpdir, [User, Document]) + router = Router(db.Routes(volume)) - self.volume['user'].create({'guid': 'user1', 'pubkey': '', 'name': 'User1'}) - guid = self.call('POST', ['document'], content={}, principal='user1') + volume['user'].create({'guid': 'user1', 'pubkey': '', 'name': 'User1'}) + guid = this.call(method='POST', path=['document'], content={}, principal='user1') - self.call('PUT', ['document', guid], cmd='useradd', user='User2', role=0) + this.call(method='PUT', path=['document', guid], cmd='useradd', user='User2', role=0) self.assertEqual([ {'guid': 'user1', 'name': 'User1', 'role': 3}, {'name': 'User2', 'role': 0}, ], - self.call('GET', ['document', guid, 'author'])) + this.call(method='GET', path=['document', guid, 'author'])) self.assertEqual({ 'user1': {'name': 'User1', 'role': 3, 'order': 0}, 'User2': {'role': 0, 'order': 1}, }, - self.volume['document'].get(guid)['author']) + volume['document'].get(guid)['author']) - self.call('PUT', ['document', guid], cmd='useradd', user='user1', role=0) + this.call(method='PUT', path=['document', guid], cmd='useradd', user='user1', role=0) self.assertEqual([ {'guid': 'user1', 'name': 'User1', 'role': 1}, {'name': 'User2', 'role': 0}, ], - self.call('GET', ['document', guid, 'author'])) + this.call(method='GET', path=['document', guid, 'author'])) self.assertEqual({ 'user1': {'name': 'User1', 'role': 1, 'order': 0}, 'User2': {'role': 0, 'order': 1}, }, - self.volume['document'].get(guid)['author']) + volume['document'].get(guid)['author']) - self.call('PUT', ['document', guid], cmd='useradd', user='User2', role=2) + this.call(method='PUT', path=['document', guid], cmd='useradd', user='User2', role=2) self.assertEqual([ {'guid': 'user1', 'name': 'User1', 'role': 1}, {'name': 'User2', 'role': 2}, ], - self.call('GET', ['document', guid, 'author'])) + this.call(method='GET', path=['document', guid, 'author'])) self.assertEqual({ 'user1': {'name': 'User1', 'role': 1, 'order': 0}, 'User2': {'role': 2, 'order': 1}, }, - self.volume['document'].get(guid)['author']) + volume['document'].get(guid)['author']) def test_DelUser(self): @@ -1494,150 +1581,73 @@ class RoutesTest(tests.Test): class Document(db.Resource): pass - self.volume = db.Volume('db', [User, Document]) + volume = db.Volume(tests.tmpdir, [User, Document]) + router = Router(db.Routes(volume)) - self.volume['user'].create({'guid': 'user1', 'pubkey': '', 'name': 'User1'}) - self.volume['user'].create({'guid': 'user2', 'pubkey': '', 'name': 'User2'}) - guid = self.call('POST', ['document'], content={}, principal='user1') - self.call('PUT', ['document', guid], cmd='useradd', user='user2') - self.call('PUT', ['document', guid], cmd='useradd', user='User3') + volume['user'].create({'guid': 'user1', 'pubkey': '', 'name': 'User1'}) + volume['user'].create({'guid': 'user2', 'pubkey': '', 'name': 'User2'}) + guid = this.call(method='POST', path=['document'], content={}, principal='user1') + this.call(method='PUT', path=['document', guid], cmd='useradd', user='user2') + this.call(method='PUT', path=['document', guid], cmd='useradd', user='User3') self.assertEqual([ {'guid': 'user1', 'name': 'User1', 'role': 3}, {'guid': 'user2', 'name': 'User2', 'role': 1}, {'name': 'User3', 'role': 0}, ], - self.call('GET', ['document', guid, 'author'])) + this.call(method='GET', path=['document', guid, 'author'])) self.assertEqual({ 'user1': {'name': 'User1', 'role': 3, 'order': 0}, 'user2': {'name': 'User2', 'role': 1, 'order': 1}, 'User3': {'role': 0, 'order': 2}, }, - self.volume['document'].get(guid)['author']) + volume['document'].get(guid)['author']) # Do not remove yourself - self.assertRaises(RuntimeError, self.call, 'PUT', ['document', guid], cmd='userdel', user='user1', principal='user1') - self.assertRaises(RuntimeError, self.call, 'PUT', ['document', guid], cmd='userdel', user='user2', principal='user2') + self.assertRaises(RuntimeError, this.call, method='PUT', path=['document', guid], cmd='userdel', user='user1', principal='user1') + self.assertRaises(RuntimeError, this.call, method='PUT', path=['document', guid], cmd='userdel', user='user2', principal='user2') - self.call('PUT', ['document', guid], cmd='userdel', user='user1', principal='user2') + this.call(method='PUT', path=['document', guid], cmd='userdel', user='user1', principal='user2') self.assertEqual([ {'guid': 'user2', 'name': 'User2', 'role': 1}, {'name': 'User3', 'role': 0}, ], - self.call('GET', ['document', guid, 'author'])) + this.call(method='GET', path=['document', guid, 'author'])) self.assertEqual({ 'user2': {'name': 'User2', 'role': 1, 'order': 1}, 'User3': {'role': 0, 'order': 2}, }, - self.volume['document'].get(guid)['author']) + volume['document'].get(guid)['author']) - self.call('PUT', ['document', guid], cmd='userdel', user='User3', principal='user2') + this.call(method='PUT', path=['document', guid], cmd='userdel', user='User3', principal='user2') self.assertEqual([ {'guid': 'user2', 'name': 'User2', 'role': 1}, ], - self.call('GET', ['document', guid, 'author'])) + this.call(method='GET', path=['document', guid, 'author'])) self.assertEqual({ 'user2': {'name': 'User2', 'role': 1, 'order': 1}, }, - self.volume['document'].get(guid)['author']) - - def test_typecast_prop_value(self): - prop = Property('prop', typecast=int) - self.assertEqual(1, _typecast_prop_value(prop.typecast, 1)) - self.assertEqual(1, _typecast_prop_value(prop.typecast, 1.1)) - self.assertEqual(1, _typecast_prop_value(prop.typecast, '1')) - self.assertRaises(ValueError, _typecast_prop_value, prop.typecast, '1.0') - self.assertRaises(ValueError, _typecast_prop_value, prop.typecast, '') - self.assertRaises(ValueError, _typecast_prop_value, prop.typecast, None) - - prop = Property('prop', typecast=float) - self.assertEqual(1.0, _typecast_prop_value(prop.typecast, 1)) - self.assertEqual(1.1, _typecast_prop_value(prop.typecast, 1.1)) - self.assertEqual(1.0, _typecast_prop_value(prop.typecast, '1')) - self.assertEqual(1.1, _typecast_prop_value(prop.typecast, '1.1')) - self.assertRaises(ValueError, _typecast_prop_value, prop.typecast, '') - self.assertRaises(ValueError, _typecast_prop_value, prop.typecast, None) - - prop = Property('prop', typecast=bool) - self.assertEqual(False, _typecast_prop_value(prop.typecast, 0)) - self.assertEqual(True, _typecast_prop_value(prop.typecast, 1)) - self.assertEqual(True, _typecast_prop_value(prop.typecast, 1.1)) - self.assertEqual(True, _typecast_prop_value(prop.typecast, '1')) - self.assertEqual(True, _typecast_prop_value(prop.typecast, 'false')) - self.assertEqual(False, _typecast_prop_value(prop.typecast, '')) - self.assertRaises(ValueError, _typecast_prop_value, prop.typecast, None) - - prop = Property('prop', typecast=[int]) - self.assertEqual((1,), _typecast_prop_value(prop.typecast, 1)) - self.assertRaises(ValueError, _typecast_prop_value, prop.typecast, None) - self.assertRaises(ValueError, _typecast_prop_value, prop.typecast, '') - self.assertEqual((), _typecast_prop_value(prop.typecast, [])) - self.assertEqual((123,), _typecast_prop_value(prop.typecast, '123')) - self.assertRaises(ValueError, _typecast_prop_value, prop.typecast, 'a') - self.assertEqual((123, 4, 5), _typecast_prop_value(prop.typecast, ['123', 4, 5.6])) - - prop = Property('prop', typecast=[1, 2]) - self.assertRaises(ValueError, _typecast_prop_value, prop.typecast, 0) - self.assertRaises(ValueError, _typecast_prop_value, prop.typecast, None) - self.assertRaises(ValueError, _typecast_prop_value, prop.typecast, '') - self.assertRaises(ValueError, _typecast_prop_value, prop.typecast, 'A') - self.assertRaises(ValueError, _typecast_prop_value, prop.typecast, '3') - self.assertEqual(1, _typecast_prop_value(prop.typecast, 1)) - self.assertEqual(2, _typecast_prop_value(prop.typecast, 2)) - self.assertEqual(1, _typecast_prop_value(prop.typecast, '1')) - - prop = Property('prop', typecast=[str]) - self.assertEqual(('',), _typecast_prop_value(prop.typecast, '')) - self.assertEqual(('',), _typecast_prop_value(prop.typecast, [''])) - self.assertEqual((), _typecast_prop_value(prop.typecast, [])) - - prop = Property('prop', typecast=[]) - self.assertRaises(ValueError, _typecast_prop_value, prop.typecast, None) - self.assertEqual(('',), _typecast_prop_value(prop.typecast, '')) - self.assertEqual(('',), _typecast_prop_value(prop.typecast, [''])) - self.assertEqual((), _typecast_prop_value(prop.typecast, [])) - self.assertEqual(('0',), _typecast_prop_value(prop.typecast, 0)) - self.assertEqual(('',), _typecast_prop_value(prop.typecast, '')) - self.assertEqual(('foo',), _typecast_prop_value(prop.typecast, 'foo')) - - prop = Property('prop', typecast=[['A', 'B', 'C']]) - self.assertRaises(ValueError, _typecast_prop_value, prop.typecast, '') - self.assertRaises(ValueError, _typecast_prop_value, prop.typecast, ['']) - self.assertEqual((), _typecast_prop_value(prop.typecast, [])) - self.assertEqual(('A', 'B', 'C'), _typecast_prop_value(prop.typecast, ['A', 'B', 'C'])) - self.assertRaises(ValueError, _typecast_prop_value, prop.typecast, ['a']) - self.assertRaises(ValueError, _typecast_prop_value, prop.typecast, ['A', 'x']) - - prop = Property('prop', typecast=bool) - self.assertEqual(True, _typecast_prop_value(prop.typecast, True)) - self.assertEqual(False, _typecast_prop_value(prop.typecast, False)) - self.assertEqual(True, _typecast_prop_value(prop.typecast, 'False')) - self.assertEqual(True, _typecast_prop_value(prop.typecast, '0')) - - prop = Property('prop', typecast=[['A', 'B', 'C']]) - self.assertEqual(('A', 'B', 'C'), _typecast_prop_value(prop.typecast, ['A', 'B', 'C'])) - - prop = Property('prop', typecast=lambda x: x + 1) - self.assertEqual(1, _typecast_prop_value(prop.typecast, 0)) + volume['document'].get(guid)['author']) def test_DefaultOrder(self): class Document(db.Resource): pass - self.volume = db.Volume('db', [Document]) + volume = db.Volume(tests.tmpdir, [Document]) + router = Router(db.Routes(volume)) - self.volume['document'].create({'guid': '3', 'ctime': 3}) - self.volume['document'].create({'guid': '2', 'ctime': 2}) - self.volume['document'].create({'guid': '1', 'ctime': 1}) + volume['document'].create({'guid': '3', 'ctime': 3}) + volume['document'].create({'guid': '2', 'ctime': 2}) + volume['document'].create({'guid': '1', 'ctime': 1}) self.assertEqual([ {'guid': '1'}, {'guid': '2'}, {'guid': '3'}, ], - self.call('GET', ['document'])['result']) + this.call(method='GET', path=['document'])['result']) - def test_SetDefaultPropsOnNoneValues(self): + def test_DefaultsOnNonePostValues(self): class Document(db.Resource): @@ -1645,10 +1655,11 @@ class RoutesTest(tests.Test): def prop(self, value): return value - self.volume = db.Volume('db', [Document]) + volume = db.Volume(tests.tmpdir, [Document]) + router = Router(db.Routes(volume)) - guid = self.call('POST', ['document'], content={'prop': None}) - self.assertEqual('default', self.volume['document'].get(guid).meta('prop')['value']) + guid = this.call(method='POST', path=['document'], content={'prop': None}) + self.assertEqual('default', this.call(method='GET', path=['document', guid, 'prop'])) def test_InsertAggprops(self): @@ -1658,117 +1669,316 @@ class RoutesTest(tests.Test): def prop1(self, value): return value - @db.stored_property(typecast=db.AggregatedType, default=db.AggregatedType(), acl=ACL.WRITE) - def prop2(self, value): - return value - - @db.stored_property(typecast=db.AggregatedType, default=db.AggregatedType(), acl=ACL.INSERT) + @db.stored_property(db.Aggregated, acl=ACL.INSERT) def prop3(self, value): return value events = [] - self.volume = db.Volume('db', [Document], lambda event: events.append(event)) - guid = self.call('POST', ['document'], content={}) + volume = db.Volume(tests.tmpdir, [Document]) + router = Router(db.Routes(volume)) + this.broadcast = lambda x: events.append(x) + guid = this.call(method='POST', path=['document'], content={}) - self.assertRaises(http.NotFound, self.call, 'POST', ['document', 'foo', 'bar'], content={}) - self.assertRaises(http.NotFound, self.call, 'POST', ['document', guid, 'bar'], content={}) - self.assertRaises(http.BadRequest, self.call, 'POST', ['document', guid, 'prop1'], content={}) - self.assertRaises(http.Forbidden, self.call, 'POST', ['document', guid, 'prop2'], content={}) + self.assertRaises(http.NotFound, this.call, method='POST', path=['document', 'foo', 'bar'], content={}) + self.assertRaises(http.NotFound, this.call, method='POST', path=['document', guid, 'bar'], content={}) + self.assertRaises(http.BadRequest, this.call, method='POST', path=['document', guid, 'prop1'], content={}) del events[:] self.override(time, 'time', lambda: 0) self.override(toolkit, 'uuid', lambda: '0') - self.assertEqual('0', self.call('POST', ['document', guid, 'prop3'], content={})) + self.assertEqual('0', this.call(method='POST', path=['document', guid, 'prop3'], content=0)) self.assertEqual({ - '0': {'seqno': 2}, + '0': {'seqno': 2, 'value': 0}, }, - self.volume['document'].get(guid)['prop3']) + volume['document'].get(guid)['prop3']) self.assertEqual([ {'event': 'update', 'resource': 'document', 'guid': guid}, ], events) self.override(time, 'time', lambda: 1) - self.assertEqual('1', self.call('POST', ['document', guid, 'prop3'], content={'guid': '1', 'foo': 'bar'})) + self.override(toolkit, 'uuid', lambda: '1') + self.assertEqual('1', this.call(method='POST', path=['document', guid, 'prop3'], content={'foo': 'bar'})) self.assertEqual({ - '0': {'seqno': 2}, - '1': {'seqno': 3, 'foo': 'bar'}, + '0': {'seqno': 2, 'value': 0}, + '1': {'seqno': 3, 'value': {'foo': 'bar'}}, }, - self.volume['document'].get(guid)['prop3']) + volume['document'].get(guid)['prop3']) self.override(time, 'time', lambda: 2) self.override(toolkit, 'uuid', lambda: '2') - self.assertEqual('2', self.call('POST', ['document', guid, 'prop3'], content={'prop': 'more'})) + self.assertEqual('2', this.call(method='POST', path=['document', guid, 'prop3'], content=None)) self.assertEqual({ - '0': {'seqno': 2}, - '1': {'seqno': 3, 'foo': 'bar'}, - '2': {'seqno': 4, 'prop': 'more'}, + '0': {'seqno': 2, 'value': 0}, + '1': {'seqno': 3, 'value': {'foo': 'bar'}}, + '2': {'seqno': 4, 'value': None}, }, - self.volume['document'].get(guid)['prop3']) + volume['document'].get(guid)['prop3']) def test_RemoveAggprops(self): class Document(db.Resource): - @db.stored_property(typecast=db.AggregatedType, default=db.AggregatedType(), acl=ACL.INSERT) + @db.stored_property(db.Aggregated, acl=ACL.INSERT) def prop1(self, value): return value - @db.stored_property(typecast=db.AggregatedType, default=db.AggregatedType(), acl=ACL.INSERT | ACL.REMOVE) + @db.stored_property(db.Aggregated, acl=ACL.INSERT | ACL.REMOVE) def prop2(self, value): return value events = [] - self.volume = db.Volume('db', [Document], lambda event: events.append(event)) - guid = self.call('POST', ['document'], content={}) + volume = db.Volume(tests.tmpdir, [Document]) + router = Router(db.Routes(volume)) + this.broadcast = lambda x: events.append(x) + guid = this.call(method='POST', path=['document'], content={}) - agg_guid = self.call('POST', ['document', guid, 'prop1'], content={'probe': 'value'}) + agg_guid = this.call(method='POST', path=['document', guid, 'prop1'], content=2) del events[:] self.assertEqual( - {agg_guid: {'seqno': 2, 'probe': 'value'}}, - self.volume['document'].get(guid)['prop1']) - self.assertRaises(http.Forbidden, self.call, 'DELETE', ['document', guid, 'prop1', agg_guid]) + {agg_guid: {'seqno': 2, 'value': 2}}, + volume['document'].get(guid)['prop1']) + self.assertRaises(http.Forbidden, this.call, method='DELETE', path=['document', guid, 'prop1', agg_guid]) self.assertEqual( - {agg_guid: {'seqno': 2, 'probe': 'value'}}, - self.volume['document'].get(guid)['prop1']) + {agg_guid: {'seqno': 2, 'value': 2}}, + volume['document'].get(guid)['prop1']) self.assertEqual([], events) - agg_guid = self.call('POST', ['document', guid, 'prop2'], content={'probe': 'value'}) + agg_guid = this.call(method='POST', path=['document', guid, 'prop2'], content=3) del events[:] self.assertEqual( - {agg_guid: {'seqno': 3, 'probe': 'value'}}, - self.volume['document'].get(guid)['prop2']) - self.call('DELETE', ['document', guid, 'prop2', agg_guid]) + {agg_guid: {'seqno': 3, 'value': 3}}, + volume['document'].get(guid)['prop2']) + this.call(method='DELETE', path=['document', guid, 'prop2', agg_guid]) self.assertEqual( {agg_guid: {'seqno': 4}}, - self.volume['document'].get(guid)['prop2']) + volume['document'].get(guid)['prop2']) + self.assertEqual([ + {'event': 'update', 'resource': 'document', 'guid': guid}, + ], + events) + + def test_FailOnAbsentAggprops(self): + + class Document(db.Resource): + + @db.stored_property(db.Aggregated, acl=ACL.INSERT | ACL.REMOVE | ACL.REPLACE) + def prop(self, value): + return value + + events = [] + volume = db.Volume(tests.tmpdir, [Document]) + router = Router(db.Routes(volume)) + this.broadcast = lambda x: events.append(x) + guid = this.call(method='POST', path=['document'], content={}) + del events[:] + + self.assertRaises(http.NotFound, this.call, method='DELETE', path=['document', guid, 'prop', 'absent']) + self.assertEqual([], events) + + def test_UpdateAggprops(self): + + class Document(db.Resource): + + @db.stored_property(db.Aggregated) + def prop1(self, value): + return value + + @db.stored_property(db.Aggregated, acl=ACL.INSERT | ACL.REMOVE | ACL.REPLACE) + def prop2(self, value): + return value + + events = [] + volume = db.Volume(tests.tmpdir, [Document]) + router = Router(db.Routes(volume)) + this.broadcast = lambda x: events.append(x) + guid = this.call(method='POST', path=['document'], content={}) + + agg_guid = this.call(method='POST', path=['document', guid, 'prop1'], content=1) + del events[:] + self.assertEqual( + {agg_guid: {'seqno': 2, 'value': 1}}, + volume['document'].get(guid)['prop1']) + self.assertRaises(http.Forbidden, this.call, method='PUT', path=['document', guid, 'prop1', agg_guid], content=2) + self.assertEqual( + {agg_guid: {'seqno': 2, 'value': 1}}, + volume['document'].get(guid)['prop1']) + self.assertEqual([], events) + + agg_guid = this.call(method='POST', path=['document', guid, 'prop2'], content=2) + del events[:] + self.assertEqual( + {agg_guid: {'seqno': 3, 'value': 2}}, + volume['document'].get(guid)['prop2']) + this.call(method='PUT', path=['document', guid, 'prop2', agg_guid], content=3) + self.assertEqual( + {agg_guid: {'seqno': 4, 'value': 3}}, + volume['document'].get(guid)['prop2']) + self.assertEqual([ + {'event': 'update', 'resource': 'document', 'guid': guid}, + ], + events) + + def test_PostAbsentAggpropsOnUpdate(self): + + class Document(db.Resource): + + @db.stored_property(db.Aggregated, acl=ACL.INSERT | ACL.REMOVE | ACL.REPLACE) + def prop(self, value): + return value + + events = [] + volume = db.Volume(tests.tmpdir, [Document]) + router = Router(db.Routes(volume)) + this.broadcast = lambda x: events.append(x) + guid = this.call(method='POST', path=['document'], content={}) + del events[:] + + this.call(method='PUT', path=['document', guid, 'prop', 'absent'], content='probe') + self.assertEqual( + {'absent': {'seqno': 2, 'value': 'probe'}}, + volume['document'].get(guid)['prop']) self.assertEqual([ {'event': 'update', 'resource': 'document', 'guid': guid}, ], events) - def call(self, method=None, path=None, - accept_language=None, content=None, content_stream=None, cmd=None, - content_type=None, host=None, request=None, routes=db.Routes, principal=None, - **kwargs): - if request is None: - environ = { - 'REQUEST_METHOD': method, - 'PATH_INFO': '/'.join([''] + path), - 'HTTP_ACCEPT_LANGUAGE': ','.join(accept_language or []), - 'HTTP_HOST': host, - 'wsgi.input': content_stream, - } - if content_type: - environ['CONTENT_TYPE'] = content_type - if content_stream is not None: - environ['CONTENT_LENGTH'] = str(len(content_stream.getvalue())) - request = Request(environ, cmd=cmd, content=content) - request.update(kwargs) - request.principal = principal - router = Router(routes(self.volume)) - self.response = Response() - return router._call_route(request, self.response) + def test_OriginalAggprops(self): + + class Document(db.Resource): + + @db.stored_property(db.Aggregated, acl=ACL.INSERT | ACL.REMOVE) + def prop(self, value): + return value + + volume = db.Volume(tests.tmpdir, [User, Document]) + router = Router(db.Routes(volume)) + volume['user'].create({'guid': 'user1', 'pubkey': '', 'name': 'User1'}) + volume['user'].create({'guid': 'user2', 'pubkey': '', 'name': 'User2'}) + + guid = this.call(method='POST', path=['document'], content={}, principal=tests.UID) + assert ACL.ORIGINAL & volume['document'][guid]['author'][tests.UID]['role'] + + agg_guid1 = this.call(method='POST', path=['document', guid, 'prop'], content=1, principal=tests.UID) + assert tests.UID2 not in volume['document'][guid]['prop'][agg_guid1]['author'] + assert ACL.ORIGINAL & volume['document'][guid]['prop'][agg_guid1]['author'][tests.UID]['role'] + + agg_guid2 = this.call(method='POST', path=['document', guid, 'prop'], content=1, principal=tests.UID2) + assert tests.UID not in volume['document'][guid]['prop'][agg_guid2]['author'] + assert not (ACL.ORIGINAL & volume['document'][guid]['prop'][agg_guid2]['author'][tests.UID2]['role']) + + this.call(method='DELETE', path=['document', guid, 'prop', agg_guid2], principal=tests.UID2) + assert tests.UID not in volume['document'][guid]['prop'][agg_guid2]['author'] + assert not (ACL.ORIGINAL & volume['document'][guid]['prop'][agg_guid2]['author'][tests.UID2]['role']) + + def test_AggregatedBlobs(self): + + class Document(db.Resource): + + @db.stored_property(db.Aggregated, subtype=db.Blob()) + def blobs(self, value): + return value + + volume = db.Volume(tests.tmpdir, [Document]) + router = Router(db.Routes(volume)) + guid = this.call(method='POST', path=['document'], content={}) + + agg1 = this.call(method='POST', path=['document', guid, 'blobs'], content='blob1') + self.assertEqual({ + agg1: {'seqno': 2, 'value': str(hash('blob1'))}, + }, + volume['document'].get(guid)['blobs']) + assert files.get(str(hash('blob1'))) + + agg2 = this.call(method='POST', path=['document', guid, 'blobs'], content='blob2') + self.assertEqual({ + agg1: {'seqno': 2, 'value': str(hash('blob1'))}, + agg2: {'seqno': 3, 'value': str(hash('blob2'))}, + }, + volume['document'].get(guid)['blobs']) + assert files.get(str(hash('blob2'))) + + this.call(method='DELETE', path=['document', guid, 'blobs', agg1]) + self.assertEqual({ + agg1: {'seqno': 4}, + agg2: {'seqno': 3, 'value': str(hash('blob2'))}, + }, + volume['document'].get(guid)['blobs']) + assert files.get(str(hash('blob1'))) is None + assert files.get(str(hash('blob2'))) + + this.call(method='DELETE', path=['document', guid, 'blobs', agg2]) + self.assertEqual({ + agg1: {'seqno': 4}, + agg2: {'seqno': 5}, + }, + volume['document'].get(guid)['blobs']) + assert files.get(str(hash('blob1'))) is None + assert files.get(str(hash('blob2'))) is None + + agg3 = this.call(method='POST', path=['document', guid, 'blobs'], content='blob3') + self.assertEqual({ + agg1: {'seqno': 4}, + agg2: {'seqno': 5}, + agg3: {'seqno': 6, 'value': str(hash('blob3'))}, + }, + volume['document'].get(guid)['blobs']) + assert files.get(str(hash('blob1'))) is None + assert files.get(str(hash('blob2'))) is None + assert files.get(str(hash('blob3'))) + + def test_AggregatedSearch(self): + + class Document(db.Resource): + + @db.stored_property(db.Aggregated, prefix='A', full_text=True) + def comments(self, value): + return value + + @db.stored_property(prefix='B', full_text=False, default='') + def prop(self, value): + return value + + volume = db.Volume(tests.tmpdir, [Document]) + router = Router(db.Routes(volume)) + + guid1 = this.call(method='POST', path=['document'], content={}) + this.call(method='POST', path=['document', guid1, 'comments'], content='a') + this.call(method='POST', path=['document', guid1, 'comments'], content='b') + this.call(method='PUT', path=['document', guid1, 'prop'], content='c') + + guid2 = this.call(method='POST', path=['document'], content={}) + this.call(method='POST', path=['document', guid2, 'comments'], content='c') + this.call(method='POST', path=['document', guid2, 'comments'], content='a') + this.call(method='PUT', path=['document', guid2, 'prop'], content='b') + + guid3 = this.call(method='POST', path=['document'], content={}) + this.call(method='POST', path=['document', guid3, 'comments'], content='a c') + this.call(method='POST', path=['document', guid3, 'comments'], content='b d') + this.call(method='PUT', path=['document', guid3, 'prop'], content='e') + + self.assertEqual( + sorted([guid1, guid2, guid3]), + sorted([i['guid'] for i in this.call(method='GET', path=['document'], query='a')['result']])) + self.assertEqual( + sorted([guid1, guid3]), + sorted([i['guid'] for i in this.call(method='GET', path=['document'], query='b')['result']])) + self.assertEqual( + sorted([guid2, guid3]), + sorted([i['guid'] for i in this.call(method='GET', path=['document'], query='c')['result']])) + self.assertEqual( + sorted([]), + sorted([i['guid'] for i in this.call(method='GET', path=['document'], query='absent')['result']])) + + self.assertEqual( + sorted([guid1, guid2, guid3]), + sorted([i['guid'] for i in this.call(method='GET', path=['document'], query='comments:a')['result']])) + self.assertEqual( + sorted([guid1, guid3]), + sorted([i['guid'] for i in this.call(method='GET', path=['document'], query='comments:b')['result']])) + self.assertEqual( + sorted([guid2, guid3]), + sorted([i['guid'] for i in this.call(method='GET', path=['document'], query='comments:c')['result']])) if __name__ == '__main__': diff --git a/tests/units/db/storage.py b/tests/units/db/storage.py index 6eb62e5..bb61f8a 100755 --- a/tests/units/db/storage.py +++ b/tests/units/db/storage.py @@ -11,8 +11,7 @@ from os.path import exists from __init__ import tests -from sugar_network.db.metadata import Metadata, StoredProperty -from sugar_network.db.metadata import BlobProperty +from sugar_network.db.metadata import Metadata, Property from sugar_network.db.storage import Storage from sugar_network.toolkit import BUFFER_SIZE @@ -30,7 +29,7 @@ class StorageTest(tests.Test): return Storage(tests.tmpdir, metadata) def test_Record_get(self): - storage = self.storage([StoredProperty('prop')]) + storage = self.storage([Property('prop')]) self.assertEqual(None, storage.get('guid').get('prop')) self.touch(('gu/guid/prop', json.dumps({ @@ -45,7 +44,7 @@ class StorageTest(tests.Test): storage.get('guid').get('prop')) def test_Record_set(self): - storage = self.storage([StoredProperty('prop')]) + storage = self.storage([Property('prop')]) storage.get('guid').set('prop', value='value', foo='bar') self.assertEqual({ @@ -56,7 +55,7 @@ class StorageTest(tests.Test): storage.get('guid').get('prop')) def test_delete(self): - storage = self.storage([StoredProperty('prop')]) + storage = self.storage([Property('prop')]) assert not exists('ab/absent') storage.delete('absent') @@ -69,8 +68,8 @@ class StorageTest(tests.Test): def test_Record_consistent(self): storage = self.storage([ - StoredProperty('guid'), - StoredProperty('prop'), + Property('guid'), + Property('prop'), ]) record = storage.get('guid') @@ -83,7 +82,7 @@ class StorageTest(tests.Test): self.assertEqual(True, record.consistent) def test_walk(self): - storage = self.storage([StoredProperty('guid')]) + storage = self.storage([Property('guid')]) storage.get('guid1').set('guid', value=1, mtime=1) storage.get('guid2').set('guid', value=2, mtime=2) @@ -107,8 +106,8 @@ class StorageTest(tests.Test): def test_walk_SkipGuidLess(self): storage = self.storage([ - StoredProperty('guid'), - StoredProperty('prop'), + Property('guid'), + Property('prop'), ]) record = storage.get('guid1') diff --git a/tests/units/model/__main__.py b/tests/units/model/__main__.py index 2766988..3814380 100644 --- a/tests/units/model/__main__.py +++ b/tests/units/model/__main__.py @@ -2,9 +2,9 @@ from __init__ import tests +from model import * from post import * from context import * -from release import * from routes import * if __name__ == '__main__': diff --git a/tests/units/model/context.py b/tests/units/model/context.py index 3389f41..71357f2 100755 --- a/tests/units/model/context.py +++ b/tests/units/model/context.py @@ -1,82 +1,331 @@ #!/usr/bin/env python # sugar-lint: disable +from cStringIO import StringIO from os.path import exists from __init__ import tests from sugar_network import db -from sugar_network.node import obs +from sugar_network.db import files +from sugar_network.client import IPCConnection, Connection, keyfile from sugar_network.model.context import Context -from sugar_network.client import IPCConnection -from sugar_network.toolkit import coroutine, enforce +from sugar_network.toolkit.coroutine import this +from sugar_network.toolkit.router import Request +from sugar_network.toolkit import i18n, http, coroutine, enforce class ContextTest(tests.Test): def test_SetCommonLayerForPackages(self): - self.start_online_client() - ipc = IPCConnection() + volume = self.start_master() + conn = Connection(auth=http.SugarAuth(keyfile.value)) - guid = ipc.post(['context'], { + guid = conn.post(['context'], { 'type': 'package', 'title': 'title', 'summary': 'summary', 'description': 'description', }) - self.assertEqual(['common'], ipc.get(['context', guid, 'layer'])) + self.assertEqual(['common'], conn.get(['context', guid, 'layer'])) - guid = ipc.post(['context'], { + guid = conn.post(['context'], { 'type': 'package', 'title': 'title', 'summary': 'summary', 'description': 'description', 'layer': 'foo', }) - self.assertEqual(['foo', 'common'], ipc.get(['context', guid, 'layer'])) + self.assertEqual(['foo', 'common'], conn.get(['context', guid, 'layer'])) - guid = ipc.post(['context'], { + guid = conn.post(['context'], { 'type': 'package', 'title': 'title', 'summary': 'summary', 'description': 'description', 'layer': ['common', 'bar'], }) - self.assertEqual(['common', 'bar'], ipc.get(['context', guid, 'layer'])) + self.assertEqual(['common', 'bar'], conn.get(['context', guid, 'layer'])) - def test_DefaultImages(self): - self.start_online_client() - ipc = IPCConnection() + def test_Releases(self): + volume = self.start_master() + conn = Connection(auth=http.SugarAuth(keyfile.value)) - guid = ipc.post(['context'], { - 'guid': 'guid', + context = conn.post(['context'], { 'type': 'activity', - 'title': 'title', + 'title': 'Activity', + 'summary': 'summary', + 'description': 'description', + }) + + activity_info1 = '\n'.join([ + '[Activity]', + 'name = Activity', + 'bundle_id = %s' % context, + 'exec = true', + 'icon = icon', + 'activity_version = 1', + 'license = Public Domain', + ]) + bundle1 = self.zips(('topdir/activity/activity.info', activity_info1)) + release1 = conn.upload(['context', context, 'releases'], StringIO(bundle1)) + assert release1 == str(hash(bundle1)) + self.assertEqual({ + release1: { + 'seqno': 5, + 'author': {tests.UID: {'name': tests.UID, 'order': 0, 'role': 3}}, + 'value': { + 'license': ['Public Domain'], + 'announce': next(volume['post'].find(query='title:1')[0]).guid, + 'release': [[1], 0], + 'requires': [], + 'spec': {'*-*': {'bundle': str(hash(bundle1)), 'commands': {'activity': {'exec': 'true'}}, 'requires': {}}}, + 'stability': 'stable', + 'unpack_size': len(activity_info1), + 'version': '1', + }, + }, + }, conn.get(['context', context, 'releases'])) + assert files.get(str(hash(bundle1))) + + activity_info2 = '\n'.join([ + '[Activity]', + 'name = Activity', + 'bundle_id = %s' % context, + 'exec = true', + 'icon = icon', + 'activity_version = 2', + 'license = Public Domain', + ]) + bundle2 = self.zips(('topdir/activity/activity.info', activity_info2)) + release2 = conn.upload(['context', context, 'releases'], StringIO(bundle2)) + assert release2 == str(hash(bundle2)) + self.assertEqual({ + release1: { + 'seqno': 5, + 'author': {tests.UID: {'name': tests.UID, 'order': 0, 'role': 3}}, + 'value': { + 'license': ['Public Domain'], + 'announce': next(volume['post'].find(query='title:1')[0]).guid, + 'release': [[1], 0], + 'requires': [], + 'spec': {'*-*': {'bundle': str(hash(bundle1)), 'commands': {'activity': {'exec': 'true'}}, 'requires': {}}}, + 'stability': 'stable', + 'unpack_size': len(activity_info1), + 'version': '1', + }, + }, + release2: { + 'seqno': 7, + 'author': {tests.UID: {'name': tests.UID, 'order': 0, 'role': 3}}, + 'value': { + 'license': ['Public Domain'], + 'announce': next(volume['post'].find(query='title:2')[0]).guid, + 'release': [[2], 0], + 'requires': [], + 'spec': {'*-*': {'bundle': str(hash(bundle2)), 'commands': {'activity': {'exec': 'true'}}, 'requires': {}}}, + 'stability': 'stable', + 'unpack_size': len(activity_info2), + 'version': '2', + }, + }, + }, conn.get(['context', context, 'releases'])) + assert files.get(str(hash(bundle1))) + assert files.get(str(hash(bundle2))) + + conn.delete(['context', context, 'releases', release1]) + self.assertEqual({ + release1: { + 'seqno': 8, + 'author': {tests.UID: {'name': tests.UID, 'order': 0, 'role': 3}}, + }, + release2: { + 'seqno': 7, + 'author': {tests.UID: {'name': tests.UID, 'order': 0, 'role': 3}}, + 'value': { + 'license': ['Public Domain'], + 'announce': next(volume['post'].find(query='title:2')[0]).guid, + 'release': [[2], 0], + 'requires': [], + 'spec': {'*-*': {'bundle': str(hash(bundle2)), 'commands': {'activity': {'exec': 'true'}}, 'requires': {}}}, + 'stability': 'stable', + 'unpack_size': len(activity_info2), + 'version': '2', + }, + }, + }, conn.get(['context', context, 'releases'])) + assert files.get(str(hash(bundle1))) is None + assert files.get(str(hash(bundle2))) + + conn.delete(['context', context, 'releases', release2]) + self.assertEqual({ + release1: { + 'seqno': 8, + 'author': {tests.UID: {'name': tests.UID, 'order': 0, 'role': 3}}, + }, + release2: { + 'seqno': 9, + 'author': {tests.UID: {'name': tests.UID, 'order': 0, 'role': 3}}, + }, + }, conn.get(['context', context, 'releases'])) + assert files.get(str(hash(bundle1))) is None + assert files.get(str(hash(bundle2))) is None + + def test_IncrementReleasesSeqnoOnNewReleases(self): + events = [] + volume = self.start_master() + this.broadcast = lambda x: events.append(x) + conn = Connection(auth=http.SugarAuth(keyfile.value)) + + context = conn.post(['context'], { + 'type': 'activity', + 'title': 'Activity', + 'summary': 'summary', + 'description': 'description', + }) + self.assertEqual([ + ], [i for i in events if i['event'] == 'release']) + self.assertEqual(0, volume.releases_seqno.value) + + conn.put(['context', context], { + 'summary': 'summary2', + }) + self.assertEqual([ + ], [i for i in events if i['event'] == 'release']) + self.assertEqual(0, volume.releases_seqno.value) + + bundle = self.zips(('topdir/activity/activity.info', '\n'.join([ + '[Activity]', + 'name = Activity', + 'bundle_id = %s' % context, + 'exec = true', + 'icon = icon', + 'activity_version = 1', + 'license = Public Domain', + ]))) + release = conn.upload(['context', context, 'releases'], StringIO(bundle)) + self.assertEqual([ + {'event': 'release', 'seqno': 1}, + ], [i for i in events if i['event'] == 'release']) + self.assertEqual(1, volume.releases_seqno.value) + + bundle = self.zips(('topdir/activity/activity.info', '\n'.join([ + '[Activity]', + 'name = Activity', + 'bundle_id = %s' % context, + 'exec = true', + 'icon = icon', + 'activity_version = 2', + 'license = Public Domain', + ]))) + release = conn.upload(['context', context, 'releases'], StringIO(bundle)) + self.assertEqual([ + {'event': 'release', 'seqno': 1}, + {'event': 'release', 'seqno': 2}, + ], [i for i in events if i['event'] == 'release']) + self.assertEqual(2, volume.releases_seqno.value) + + bundle = self.zips(('topdir/activity/activity.info', '\n'.join([ + '[Activity]', + 'name = Activity', + 'bundle_id = %s' % context, + 'exec = true', + 'icon = icon', + 'activity_version = 2', + 'license = Public Domain', + ]))) + release = conn.upload(['context', context, 'releases'], StringIO(bundle)) + self.assertEqual([ + {'event': 'release', 'seqno': 1}, + {'event': 'release', 'seqno': 2}, + {'event': 'release', 'seqno': 3}, + ], [i for i in events if i['event'] == 'release']) + self.assertEqual(3, volume.releases_seqno.value) + + conn.delete(['context', context, 'releases', release]) + self.assertEqual([ + {'event': 'release', 'seqno': 1}, + {'event': 'release', 'seqno': 2}, + {'event': 'release', 'seqno': 3}, + {'event': 'release', 'seqno': 4}, + ], [i for i in events if i['event'] == 'release']) + self.assertEqual(4, volume.releases_seqno.value) + + def test_IncrementReleasesSeqnoOnDependenciesChange(self): + events = [] + volume = self.start_master() + this.broadcast = lambda x: events.append(x) + conn = Connection(auth=http.SugarAuth(keyfile.value)) + + context = conn.post(['context'], { + 'type': 'activity', + 'title': 'Activity', 'summary': 'summary', 'description': 'description', }) - assert exists('master/context/gu/guid/artifact_icon.blob') - assert exists('master/context/gu/guid/icon.blob') - assert exists('master/context/gu/guid/logo.blob') - - def test_RatingSort(self): - directory = db.Volume('db', [Context])['context'] - - directory.create({'guid': '1', 'type': 'activity', 'title': '', 'summary': '', 'description': '', 'rating': [0, 0]}) - directory.create({'guid': '2', 'type': 'activity', 'title': '', 'summary': '', 'description': '', 'rating': [1, 2]}) - directory.create({'guid': '3', 'type': 'activity', 'title': '', 'summary': '', 'description': '', 'rating': [1, 4]}) - directory.create({'guid': '4', 'type': 'activity', 'title': '', 'summary': '', 'description': '', 'rating': [10, 10]}) - directory.create({'guid': '5', 'type': 'activity', 'title': '', 'summary': '', 'description': '', 'rating': [30, 90]}) - - self.assertEqual( - ['1', '2', '3', '4', '5'], - [i.guid for i in directory.find()[0]]) - self.assertEqual( - ['1', '4', '2', '5', '3'], - [i.guid for i in directory.find(order_by='rating')[0]]) - self.assertEqual( - ['3', '5', '2', '4', '1'], - [i.guid for i in directory.find(order_by='-rating')[0]]) + self.assertEqual([ + ], [i for i in events if i['event'] == 'release']) + self.assertEqual(0, volume.releases_seqno.value) + + conn.put(['context', context], { + 'dependencies': 'dep', + }) + self.assertEqual([ + {'event': 'release', 'seqno': 1}, + ], [i for i in events if i['event'] == 'release']) + self.assertEqual(1, volume.releases_seqno.value) + + def test_IncrementReleasesSeqnoOnDeletes(self): + events = [] + volume = self.start_master() + this.broadcast = lambda x: events.append(x) + conn = Connection(auth=http.SugarAuth(keyfile.value)) + + context = conn.post(['context'], { + 'type': 'activity', + 'title': 'Activity', + 'summary': 'summary', + 'description': 'description', + }) + self.assertEqual([ + ], [i for i in events if i['event'] == 'release']) + self.assertEqual(0, volume.releases_seqno.value) + + conn.put(['context', context], { + 'layer': ['deleted'], + }) + self.assertEqual([ + {'event': 'release', 'seqno': 1}, + ], [i for i in events if i['event'] == 'release']) + self.assertEqual(1, volume.releases_seqno.value) + + conn.put(['context', context], { + 'layer': [], + }) + self.assertEqual([ + {'event': 'release', 'seqno': 1}, + {'event': 'release', 'seqno': 2}, + ], [i for i in events if i['event'] == 'release']) + self.assertEqual(2, volume.releases_seqno.value) + + def test_RestoreReleasesSeqno(self): + events = [] + volume = self.start_master() + this.broadcast = lambda x: events.append(x) + conn = Connection(auth=http.SugarAuth(keyfile.value)) + + context = conn.post(['context'], { + 'type': 'activity', + 'title': 'Activity', + 'summary': 'summary', + 'description': 'description', + 'dependencies': 'dep', + }) + self.assertEqual(1, volume.releases_seqno.value) + + volume.close() + volume = db.Volume('master', []) + self.assertEqual(1, volume.releases_seqno.value) if __name__ == '__main__': diff --git a/tests/units/model/model.py b/tests/units/model/model.py new file mode 100755 index 0000000..f8b3866 --- /dev/null +++ b/tests/units/model/model.py @@ -0,0 +1,519 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# sugar-lint: disable + +import base64 + +from __init__ import tests + +from sugar_network import db +from sugar_network.db import files +from sugar_network.model import load_bundle +from sugar_network.model.post import Post +from sugar_network.client import IPCConnection, Connection, keyfile +from sugar_network.toolkit.router import Request +from sugar_network.toolkit.coroutine import this +from sugar_network.toolkit import i18n, http, coroutine, enforce + + +class ModelTest(tests.Test): + + def test_RatingSort(self): + directory = db.Volume('db', [Post])['post'] + + directory.create({'guid': '1', 'context': '', 'type': 'post', 'title': {}, 'message': {}, 'rating': [0, 0]}) + directory.create({'guid': '2', 'context': '', 'type': 'post', 'title': {}, 'message': {}, 'rating': [1, 2]}) + directory.create({'guid': '3', 'context': '', 'type': 'post', 'title': {}, 'message': {}, 'rating': [1, 4]}) + directory.create({'guid': '4', 'context': '', 'type': 'post', 'title': {}, 'message': {}, 'rating': [10, 10]}) + directory.create({'guid': '5', 'context': '', 'type': 'post', 'title': {}, 'message': {}, 'rating': [30, 90]}) + + self.assertEqual( + ['1', '2', '3', '4', '5'], + [i.guid for i in directory.find()[0]]) + self.assertEqual( + ['1', '4', '2', '5', '3'], + [i.guid for i in directory.find(order_by='rating')[0]]) + self.assertEqual( + ['3', '5', '2', '4', '1'], + [i.guid for i in directory.find(order_by='-rating')[0]]) + + def test_load_bundle_Activity(self): + volume = self.start_master() + conn = Connection(auth=http.SugarAuth(keyfile.value)) + + conn.post(['context'], { + 'guid': 'bundle_id', + 'type': 'activity', + 'title': 'Activity', + 'summary': 'summary', + 'description': 'description', + }) + activity_info = '\n'.join([ + '[Activity]', + 'name = Activity', + 'bundle_id = bundle_id', + 'exec = true', + 'icon = icon', + 'activity_version = 1', + 'license = Public Domain', + 'stability = developer', + 'requires = sugar>=0.88; dep' + ]) + changelog = "LOG" + bundle = self.zips( + ('topdir/activity/activity.info', activity_info), + ('topdir/CHANGELOG', changelog), + ) + blob = files.post(bundle) + + this.request = Request(method='POST', path=['context', 'bundle_id'], principal=tests.UID) + context, release = load_bundle(blob, 'bundle_id') + + self.assertEqual({ + 'mime_type': 'application/vnd.olpc-sugar', + 'name': 'Activity-1', + }, files.get(blob.digest)) + self.assertEqual('bundle_id', context) + self.assertEqual('1', release['version']) + self.assertEqual('developer', release['stability']) + self.assertEqual(['Public Domain'], release['license']) + self.assertEqual('developer', release['stability']) + self.assertEqual(sorted(['dep', 'sugar-0.88']), sorted(release['requires'])) + self.assertEqual({ + '*-*': { + 'bundle': blob.digest, + 'commands': {'activity': {'exec': 'true'}}, + 'requires': {'dep': {}, 'sugar': {'restrictions': [('0.88', None)]}}, + }, + }, + release['spec']) + self.assertEqual(len(activity_info) + len(changelog), release['unpack_size']) + + post = volume['post'][release['announce']] + assert tests.UID in post['author'] + self.assertEqual('notification', post['type']) + self.assertEqual({ + 'en': 'Activity 1 release', + 'es': 'Activity 1 release', + 'fr': 'Activity 1 release', + }, post['title']) + self.assertEqual({ + 'en-us': 'LOG', + }, post['message']) + + def test_load_bundle_NonActivity(self): + volume = self.start_master() + conn = Connection(auth=http.SugarAuth(keyfile.value)) + + conn.post(['context'], { + 'guid': 'bundle_id', + 'type': 'book', + 'title': 'NonActivity', + 'summary': 'summary', + 'description': 'description', + }) + bundle = 'non-activity' + blob = files.post(bundle) + + this.request = Request(method='POST', path=['context', 'bundle_id'], principal=tests.UID, + content_type = 'content/type', version='2', license='GPL') + context, release = load_bundle(blob, 'bundle_id') + + self.assertEqual({ + 'mime_type': 'content/type', + 'name': 'NonActivity-2', + }, files.get(blob.digest)) + self.assertEqual('bundle_id', context) + self.assertEqual('2', release['version']) + self.assertEqual(['GPL'], release['license']) + + post = volume['post'][release['announce']] + assert tests.UID in post['author'] + self.assertEqual('notification', post['type']) + self.assertEqual({ + 'en': 'NonActivity 2 release', + 'es': 'NonActivity 2 release', + 'fr': 'NonActivity 2 release', + }, post['title']) + self.assertEqual({ + 'en-us': '', + }, post['message']) + + def test_load_bundle_ReuseActivityLicense(self): + volume = self.start_master() + conn = Connection(auth=http.SugarAuth(keyfile.value)) + + conn.post(['context'], { + 'guid': 'bundle_id', + 'type': 'activity', + 'title': 'Activity', + 'summary': 'summary', + 'description': 'description', + }) + + activity_info_wo_license = '\n'.join([ + '[Activity]', + 'name = Activity', + 'bundle_id = bundle_id', + 'exec = true', + 'icon = icon', + 'activity_version = 1', + ]) + bundle = self.zips(('topdir/activity/activity.info', activity_info_wo_license)) + blob_wo_license = files.post(bundle) + self.assertRaises(http.BadRequest, load_bundle, blob_wo_license, 'bundle_id') + + volume['context'].update('bundle_id', {'releases': { + 'new': {'release': 2, 'license': ['New']}, + }}) + this.request = Request(method='POST', path=['context', 'bundle_id'], principal=tests.UID) + context, release = load_bundle(blob_wo_license, 'bundle_id') + self.assertEqual(['New'], release['license']) + + volume['context'].update('bundle_id', {'releases': { + 'new': {'release': 2, 'license': ['New']}, + 'old': {'release': 1, 'license': ['Old']}, + }}) + this.request = Request(method='POST', path=['context', 'bundle_id'], principal=tests.UID) + context, release = load_bundle(blob_wo_license, 'bundle_id') + self.assertEqual(['New'], release['license']) + + volume['context'].update('bundle_id', {'releases': { + 'new': {'release': 2, 'license': ['New']}, + 'old': {'release': 1, 'license': ['Old']}, + 'newest': {'release': 3, 'license': ['Newest']}, + }}) + this.request = Request(method='POST', path=['context', 'bundle_id'], principal=tests.UID) + context, release = load_bundle(blob_wo_license, 'bundle_id') + self.assertEqual(['Newest'], release['license']) + + def test_load_bundle_ReuseNonActivityLicense(self): + volume = self.start_master() + conn = Connection(auth=http.SugarAuth(keyfile.value)) + + conn.post(['context'], { + 'guid': 'bundle_id', + 'type': 'book', + 'title': 'Activity', + 'summary': 'summary', + 'description': 'description', + }) + + blob = files.post('non-activity') + this.request = Request(method='POST', path=['context', 'bundle_id'], principal=tests.UID, version='1') + self.assertRaises(http.BadRequest, load_bundle, blob, 'bundle_id') + + volume['context'].update('bundle_id', {'releases': { + 'new': {'release': 2, 'license': ['New']}, + }}) + this.request = Request(method='POST', path=['context', 'bundle_id'], principal=tests.UID, version='1') + context, release = load_bundle(blob, 'bundle_id') + self.assertEqual(['New'], release['license']) + + volume['context'].update('bundle_id', {'releases': { + 'new': {'release': 2, 'license': ['New']}, + 'old': {'release': 1, 'license': ['Old']}, + }}) + this.request = Request(method='POST', path=['context', 'bundle_id'], principal=tests.UID, version='1') + context, release = load_bundle(blob, 'bundle_id') + self.assertEqual(['New'], release['license']) + + volume['context'].update('bundle_id', {'releases': { + 'new': {'release': 2, 'license': ['New']}, + 'old': {'release': 1, 'license': ['Old']}, + 'newest': {'release': 3, 'license': ['Newest']}, + }}) + this.request = Request(method='POST', path=['context', 'bundle_id'], principal=tests.UID, version='1') + context, release = load_bundle(blob, 'bundle_id') + self.assertEqual(['Newest'], release['license']) + + def test_load_bundle_WrontContextType(self): + volume = self.start_master() + conn = Connection(auth=http.SugarAuth(keyfile.value)) + + conn.post(['context'], { + 'guid': 'bundle_id', + 'type': 'group', + 'title': 'NonActivity', + 'summary': 'summary', + 'description': 'description', + }) + + blob = files.post('non-activity') + this.request = Request(method='POST', path=['context', 'bundle_id'], principal=tests.UID, version='2', license='GPL') + self.assertRaises(http.BadRequest, load_bundle, blob, 'bundle_id') + + activity_info = '\n'.join([ + '[Activity]', + 'name = Activity', + 'bundle_id = bundle_id', + 'exec = true', + 'icon = icon', + 'activity_version = 1', + 'license = Public Domain', + 'stability = developer', + 'requires = sugar>=0.88; dep' + ]) + changelog = "LOG" + bundle = self.zips( + ('topdir/activity/activity.info', activity_info), + ('topdir/CHANGELOG', changelog), + ) + blob = files.post(bundle) + self.assertRaises(http.BadRequest, load_bundle, blob, 'bundle_id') + + def test_load_bundle_MissedContext(self): + volume = self.start_master() + volume['user'].create({'guid': tests.UID, 'name': 'user', 'pubkey': tests.PUBKEY}) + conn = Connection(auth=http.SugarAuth(keyfile.value)) + + bundle = self.zips(('topdir/activity/activity.info', '\n'.join([ + '[Activity]', + 'name = Activity', + 'bundle_id = bundle_id', + 'exec = true', + 'icon = icon', + 'activity_version = 1', + 'license = Public Domain', + 'stability = developer', + 'requires = sugar>=0.88; dep' + ]))) + blob = files.post(bundle) + + this.request = Request(principal=tests.UID) + self.assertRaises(http.NotFound, load_bundle, blob, initial=False) + + def test_load_bundle_CreateContext(self): + volume = self.start_master() + volume['user'].create({'guid': tests.UID, 'name': 'user', 'pubkey': tests.PUBKEY}) + conn = Connection(auth=http.SugarAuth(keyfile.value)) + + bundle = self.zips( + ('ImageViewer.activity/activity/activity.info', '\n'.join([ + '[Activity]', + 'bundle_id = org.laptop.ImageViewerActivity', + 'name = Image Viewer', + 'summary = The Image Viewer activity is a simple and fast image viewer tool', + 'description = It has features one would expect of a standard image viewer, like zoom, rotate, etc.', + 'homepage = http://wiki.sugarlabs.org/go/Activities/Image_Viewer', + 'activity_version = 1', + 'license = GPLv2+', + 'icon = activity-imageviewer', + 'exec = true', + 'mime_types = image/bmp;image/gif', + ])), + ('ImageViewer.activity/activity/activity-imageviewer.svg', ''), + ) + blob = files.post(bundle) + + this.request = Request(principal=tests.UID) + context, release = load_bundle(blob, initial=True) + self.assertEqual('org.laptop.ImageViewerActivity', context) + + context = volume['context'].get('org.laptop.ImageViewerActivity') + self.assertEqual({'en': 'Image Viewer'}, context['title']) + self.assertEqual({'en': 'The Image Viewer activity is a simple and fast image viewer tool'}, context['summary']) + self.assertEqual({'en': 'It has features one would expect of a standard image viewer, like zoom, rotate, etc.'}, context['description']) + self.assertEqual('http://wiki.sugarlabs.org/go/Activities/Image_Viewer', context['homepage']) + self.assertEqual(['image/bmp', 'image/gif'], context['mime_types']) + assert context['ctime'] > 0 + assert context['mtime'] > 0 + self.assertEqual({tests.UID: {'role': 3, 'name': 'user', 'order': 0}}, context['author']) + + post = volume['post'][release['announce']] + assert tests.UID in post['author'] + self.assertEqual('notification', post['type']) + self.assertEqual({ + 'en': 'Image Viewer 1 release', + 'es': 'Image Viewer 1 release', + 'fr': 'Image Viewer 1 release', + }, post['title']) + + def test_load_bundle_UpdateContext(self): + volume = self.start_master() + conn = Connection(auth=http.SugarAuth(keyfile.value)) + + conn.post(['context'], { + 'guid': 'org.laptop.ImageViewerActivity', + 'type': 'activity', + 'title': {'en': ''}, + 'summary': {'en': ''}, + 'description': {'en': ''}, + }) + svg = '\n'.join([ + '<?xml version="1.0" encoding="UTF-8"?>', + '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [', + ' <!ENTITY fill_color "#123456">', + ' <!ENTITY stroke_color "#123456">', + ']>', + '<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50">', + ' <rect x="3" y="7" width="44" height="36" style="fill:&fill_color;;stroke:&stroke_color;;stroke-width:3"/>', + ' <polyline points="15,7 25,1 35,7" style="fill:none;;stroke:&stroke_color;;stroke-width:1.25"/>', + ' <circle cx="14" cy="19" r="4.5" style="fill:&stroke_color;;stroke:&stroke_color;;stroke-width:1.5"/>', + ' <polyline points="3,36 16,32 26,35" style="fill:none;;stroke:&stroke_color;;stroke-width:2.5"/>', + ' <polyline points="15,43 37,28 47,34 47,43" style="fill:&stroke_color;;stroke:&stroke_color;;stroke-width:3"/>', + ' <polyline points="22,41.5 35,30 27,41.5" style="fill:&fill_color;;stroke:none;;stroke-width:0"/>', + ' <polyline points="26,23 28,25 30,23" style="fill:none;;stroke:&stroke_color;;stroke-width:.9"/>', + ' <polyline points="31.2,20 33.5,17.7 35.8,20" style="fill:none;;stroke:&stroke_color;;stroke-width:1"/>', + ' <polyline points="36,13 38.5,15.5 41,13" style="fill:none;;stroke:&stroke_color;;stroke-width:1"/>', + '</svg>', + ]) + bundle = self.zips( + ('ImageViewer.activity/activity/activity.info', '\n'.join([ + '[Activity]', + 'bundle_id = org.laptop.ImageViewerActivity', + 'name = Image Viewer', + 'summary = The Image Viewer activity is a simple and fast image viewer tool', + 'description = It has features one would expect of a standard image viewer, like zoom, rotate, etc.', + 'homepage = http://wiki.sugarlabs.org/go/Activities/Image_Viewer', + 'activity_version = 22', + 'license = GPLv2+', + 'icon = activity-imageviewer', + 'exec = true', + 'mime_types = image/bmp;image/gif', + ])), + ('ImageViewer.activity/locale/ru/LC_MESSAGES/org.laptop.ImageViewerActivity.mo', + base64.b64decode('3hIElQAAAAAMAAAAHAAAAHwAAAARAAAA3AAAAAAAAAAgAQAADwAAACEBAAAOAAAAMQEAAA0AAABAAQAACgAAAE4BAAAMAAAAWQEAAA0AAABmAQAAJwAAAHQBAAAUAAAAnAEAABAAAACxAQAABwAAAMIBAAAIAAAAygEAANEBAADTAQAAIQAAAKUDAAATAAAAxwMAABwAAADbAwAAFwAAAPgDAAAhAAAAEAQAAB0AAAAyBAAAQAAAAFAEAAA9AAAAkQQAADUAAADPBAAAFAAAAAUFAAAQAAAAGgUAAAEAAAACAAAABwAAAAAAAAADAAAAAAAAAAwAAAAJAAAAAAAAAAoAAAAEAAAAAAAAAAAAAAALAAAABgAAAAgAAAAFAAAAAENob29zZSBkb2N1bWVudABEb3dubG9hZGluZy4uLgBGaXQgdG8gd2luZG93AEZ1bGxzY3JlZW4ASW1hZ2UgVmlld2VyAE9yaWdpbmFsIHNpemUAUmV0cmlldmluZyBzaGFyZWQgaW1hZ2UsIHBsZWFzZSB3YWl0Li4uAFJvdGF0ZSBhbnRpY2xvY2t3aXNlAFJvdGF0ZSBjbG9ja3dpc2UAWm9vbSBpbgBab29tIG91dABQcm9qZWN0LUlkLVZlcnNpb246IFBBQ0tBR0UgVkVSU0lPTgpSZXBvcnQtTXNnaWQtQnVncy1UbzogClBPVC1DcmVhdGlvbi1EYXRlOiAyMDEyLTA5LTI3IDE0OjU3LTA0MDAKUE8tUmV2aXNpb24tRGF0ZTogMjAxMC0wOS0yMiAxMzo1MCswMjAwCkxhc3QtVHJhbnNsYXRvcjoga3JvbTlyYSA8a3JvbTlyYUBnbWFpbC5jb20+Ckxhbmd1YWdlLVRlYW06IExBTkdVQUdFIDxMTEBsaS5vcmc+Ckxhbmd1YWdlOiAKTUlNRS1WZXJzaW9uOiAxLjAKQ29udGVudC1UeXBlOiB0ZXh0L3BsYWluOyBjaGFyc2V0PVVURi04CkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6IDhiaXQKUGx1cmFsLUZvcm1zOiBucGx1cmFscz0zOyBwbHVyYWw9KG4lMTA9PTEgJiYgbiUxMDAhPTExID8gMCA6IG4lMTA+PTIgJiYgbiUxMDw9NCAmJiAobiUxMDA8MTAgfHwgbiUxMDA+PTIwKSA/IDEgOiAyKTsKWC1HZW5lcmF0b3I6IFBvb3RsZSAyLjAuMwoA0JLRi9Cx0LXRgNC40YLQtSDQtNC+0LrRg9C80LXQvdGCANCX0LDQs9GA0YPQt9C60LAuLi4A0KPQvNC10YHRgtC40YLRjCDQsiDQvtC60L3QtQDQn9C+0LvQvdGL0Lkg0Y3QutGA0LDQvQDQn9GA0L7RgdC80L7RgtGAINC60LDRgNGC0LjQvdC+0LoA0JjRgdGC0LjQvdC90YvQuSDRgNCw0LfQvNC10YAA0J/QvtC70YPRh9C10L3QuNC1INC40LfQvtCx0YDQsNC20LXQvdC40LksINC/0L7QtNC+0LbQtNC40YLQtS4uLgDQn9C+0LLQtdGA0L3Rg9GC0Ywg0L/RgNC+0YLQuNCyINGH0LDRgdC+0LLQvtC5INGB0YLRgNC10LvQutC4ANCf0L7QstC10YDQvdGD0YLRjCDQv9C+INGH0LDRgdC+0LLQvtC5INGB0YLRgNC10LvQutC1ANCf0YDQuNCx0LvQuNC30LjRgtGMANCe0YLQtNCw0LvQuNGC0YwA')), + ('ImageViewer.activity/activity/activity-imageviewer.svg', svg), + ) + + blob = files.post(bundle) + this.request = Request(method='POST', path=['context', 'org.laptop.ImageViewerActivity'], principal=tests.UID) + context, release = load_bundle(blob, initial=True) + + context = volume['context'].get('org.laptop.ImageViewerActivity') + self.assertEqual({ + 'en': 'Image Viewer', + 'ru': u'Просмотр картинок', + }, + context['title']) + self.assertEqual({ + 'en': 'The Image Viewer activity is a simple and fast image viewer tool', + }, + context['summary']) + self.assertEqual({ + 'en': 'It has features one would expect of a standard image viewer, like zoom, rotate, etc.', + }, + context['description']) + self.assertEqual(svg, file(files.get(context['artifact_icon']).path).read()) + assert context['icon'] != 'missing.png' + assert context['logo'] != 'missing-logo.png' + self.assertEqual('http://wiki.sugarlabs.org/go/Activities/Image_Viewer', context['homepage']) + self.assertEqual(['image/bmp', 'image/gif'], context['mime_types']) + + def test_load_bundle_3rdPartyRelease(self): + i18n._default_langs = ['en'] + volume = self.start_master() + volume['user'].create({'guid': tests.UID2, 'name': 'user2', 'pubkey': tests.PUBKEY2}) + conn = Connection(auth=http.SugarAuth(keyfile.value)) + + conn.post(['context'], { + 'guid': 'bundle_id', + 'type': 'activity', + 'title': 'Activity', + 'summary': 'summary', + 'description': 'description', + }) + + bundle = self.zips(('topdir/activity/activity.info', '\n'.join([ + '[Activity]', + 'name = Activity2', + 'bundle_id = bundle_id', + 'exec = true', + 'icon = icon', + 'activity_version = 1', + 'license = Public Domain', + 'stability = developer', + ]))) + blob = files.post(bundle) + this.request = Request(method='POST', path=['context', 'bundle_id'], principal=tests.UID2) + context, release = load_bundle(blob, 'bundle_id') + + assert tests.UID in volume['context']['bundle_id']['author'] + assert tests.UID2 not in volume['context']['bundle_id']['author'] + self.assertEqual({'en': 'Activity'}, volume['context']['bundle_id']['title']) + + post = volume['post'][release['announce']] + assert tests.UID not in post['author'] + assert tests.UID2 in post['author'] + self.assertEqual('notification', post['type']) + self.assertEqual({ + 'en': 'Activity 1 third-party release', + 'es': 'Activity 1 third-party release', + 'fr': 'Activity 1 third-party release', + }, post['title']) + + files.delete(blob.digest) + blob = files.post(bundle) + this.request = Request(method='POST', path=['context', 'bundle_id'], principal=tests.UID) + context, release = load_bundle(blob, 'bundle_id') + + assert tests.UID in volume['context']['bundle_id']['author'] + assert tests.UID2 not in volume['context']['bundle_id']['author'] + self.assertEqual({'en': 'Activity2'}, volume['context']['bundle_id']['title']) + + post = volume['post'][release['announce']] + assert tests.UID in post['author'] + assert tests.UID2 not in post['author'] + self.assertEqual('notification', post['type']) + self.assertEqual({ + 'en': 'Activity2 1 release', + 'es': 'Activity2 1 release', + 'fr': 'Activity2 1 release', + }, post['title']) + + def test_load_bundle_PopulateRequires(self): + volume = self.start_master() + conn = Connection(auth=http.SugarAuth(keyfile.value)) + + conn.post(['context'], { + 'guid': 'bundle_id', + 'type': 'activity', + 'title': 'Activity', + 'summary': 'summary', + 'description': 'description', + }) + bundle = self.zips( + ('ImageViewer.activity/activity/activity.info', '\n'.join([ + '[Activity]', + 'bundle_id = bundle_id', + 'name = Image Viewer', + 'activity_version = 22', + 'license = GPLv2+', + 'icon = activity-imageviewer', + 'exec = true', + 'requires = dep1, dep2<10, dep3<=20, dep4>30, dep5>=40, dep6>5<7, dep7>=1<=3', + ])), + ('ImageViewer.activity/activity/activity-imageviewer.svg', ''), + ) + blob = files.post(bundle) + this.request = Request(method='POST', path=['context', 'bundle_id'], principal=tests.UID) + context, release = load_bundle(blob, 'bundle_id') + + self.assertEqual( + sorted([ + 'dep1', 'dep2', 'dep3', 'dep4-31', 'dep5-40', + 'dep6-6', + 'dep7-1', 'dep7-2', 'dep7-3', + ]), + sorted(release['requires'])) + + def test_load_bundle_IgnoreNotSupportedContextTypes(self): + volume = self.start_master() + conn = Connection(auth=http.SugarAuth(keyfile.value)) + + context = conn.post(['context'], { + 'type': 'package', + 'title': '', + 'summary': '', + 'description': '', + }) + this.request = Request(method='POST', path=['context', context]) + aggid = conn.post(['context', context, 'releases'], -1) + self.assertEqual({ + aggid: {'seqno': 3, 'value': -1, 'author': {tests.UID: {'role': 3, 'name': tests.UID, 'order': 0}}}, + }, volume['context'][context]['releases']) + + +if __name__ == '__main__': + tests.main() diff --git a/tests/units/model/post.py b/tests/units/model/post.py index dc6f6f4..45b85e1 100755 --- a/tests/units/model/post.py +++ b/tests/units/model/post.py @@ -8,74 +8,27 @@ from sugar_network.client import Connection, keyfile from sugar_network.model.user import User from sugar_network.model.context import Context from sugar_network.model.post import Post -from sugar_network.model.release import Release +from sugar_network.toolkit.coroutine import this from sugar_network.toolkit import http class PostTest(tests.Test): - def test_SetContext(self): - volume = self.start_master([User, Context, Release, Post]) - client = Connection(auth=http.SugarAuth(keyfile.value)) - - self.assertRaises(http.NotFound, client.post, ['post'], {'type': 'comment', 'title': '', 'message': '', 'topic': 'absent'}) - - context = client.post(['context'], { - 'type': 'package', - 'title': 'title', - 'summary': 'summary', - 'description': 'description', - }) - topic = client.post(['post'], { - 'context': context, - 'title': 'title', - 'message': 'message', - 'type': 'update', - }) - comment = client.post(['post'], { - 'topic': topic, - 'title': 'title', - 'message': 'message', - 'type': 'comment', - }) - self.assertEqual( - context, - client.get(['post', comment, 'context'])) - - def test_RatingSort(self): - directory = db.Volume('db', [Post])['post'] - - directory.create({'guid': '1', 'context': '', 'type': 'comment', 'title': '', 'message': '', 'rating': [0, 0]}) - directory.create({'guid': '2', 'context': '', 'type': 'comment', 'title': '', 'message': '', 'rating': [1, 2]}) - directory.create({'guid': '3', 'context': '', 'type': 'comment', 'title': '', 'message': '', 'rating': [1, 4]}) - directory.create({'guid': '4', 'context': '', 'type': 'comment', 'title': '', 'message': '', 'rating': [10, 10]}) - directory.create({'guid': '5', 'context': '', 'type': 'comment', 'title': '', 'message': '', 'rating': [30, 90]}) - - self.assertEqual( - ['1', '2', '3', '4', '5'], - [i.guid for i in directory.find()[0]]) - self.assertEqual( - ['1', '4', '2', '5', '3'], - [i.guid for i in directory.find(order_by='rating')[0]]) - self.assertEqual( - ['3', '5', '2', '4', '1'], - [i.guid for i in directory.find(order_by='-rating')[0]]) - def test_FindComments(self): directory = db.Volume('db', [Post])['post'] - directory.create({'guid': '1', 'context': '', 'type': 'comment', 'title': '', 'message': '', 'comments': { - '1': {'message': 'foo'}, + directory.create({'guid': '1', 'context': '', 'type': 'post', 'title': {}, 'message': {}, 'comments': { + '1': {'value': {'en': 'foo'}}, }}) - directory.create({'guid': '2', 'context': '', 'type': 'comment', 'title': '', 'message': '', 'comments': { - '1': {'message': 'bar'}, + directory.create({'guid': '2', 'context': '', 'type': 'post', 'title': {}, 'message': {}, 'comments': { + '1': {'value': {'en': 'bar'}}, }}) - directory.create({'guid': '3', 'context': '', 'type': 'comment', 'title': '', 'message': '', 'comments': { - '1': {'message': 'bar'}, - '2': {'message': 'foo'}, + directory.create({'guid': '3', 'context': '', 'type': 'post', 'title': {}, 'message': {}, 'comments': { + '1': {'value': {'en': 'bar'}}, + '2': {'value': {'en': 'foo'}}, }}) - directory.create({'guid': '4', 'context': '', 'type': 'comment', 'title': '', 'message': '', 'comments': { - '1': {'message': 'foo bar'}, + directory.create({'guid': '4', 'context': '', 'type': 'post', 'title': {}, 'message': {}, 'comments': { + '1': {'value': {'en': 'foo bar'}}, }}) self.assertEqual( @@ -92,6 +45,115 @@ class PostTest(tests.Test): ['1', '3', '4'], [i.guid for i in directory.find(query='comments:foo')[0]]) + def test_ShiftContextRating(self): + volume = db.Volume('db', [Context, Post]) + this.volume = volume + + context = volume['context'].create({ + 'type': 'activity', + 'title': {}, + 'summary': {}, + 'description': {}, + }) + self.assertEqual([0, 0], volume['context'][context]['rating']) + + volume['post'].create({ + 'context': context, + 'type': 'post', + 'title': {}, + 'message': {}, + }) + self.assertEqual([0, 0], volume['context'][context]['rating']) + + volume['post'].create({ + 'context': context, + 'type': 'post', + 'title': {}, + 'message': {}, + 'vote': 0, + }) + self.assertEqual([0, 0], volume['context'][context]['rating']) + + volume['post'].create({ + 'context': context, + 'type': 'post', + 'title': {}, + 'message': {}, + 'vote': 1, + }) + self.assertEqual([1, 1], volume['context'][context]['rating']) + + volume['post'].create({ + 'context': context, + 'type': 'post', + 'title': {}, + 'message': {}, + 'vote': 2, + }) + self.assertEqual([2, 3], volume['context'][context]['rating']) + + def test_ShiftTopicRating(self): + volume = db.Volume('db2', [Context, Post]) + this.volume = volume + + context = volume['context'].create({ + 'type': 'activity', + 'title': {}, + 'summary': {}, + 'description': {}, + }) + topic = volume['post'].create({ + 'context': context, + 'type': 'post', + 'title': {}, + 'message': {}, + }) + self.assertEqual([0, 0], volume['context'][context]['rating']) + self.assertEqual([0, 0], volume['post'][topic]['rating']) + + volume['post'].create({ + 'context': context, + 'topic': topic, + 'type': 'post', + 'title': {}, + 'message': {}, + }) + self.assertEqual([0, 0], volume['context'][context]['rating']) + self.assertEqual([0, 0], volume['post'][topic]['rating']) + + volume['post'].create({ + 'context': context, + 'topic': topic, + 'type': 'post', + 'title': {}, + 'message': {}, + 'vote': 0, + }) + self.assertEqual([0, 0], volume['context'][context]['rating']) + self.assertEqual([0, 0], volume['post'][topic]['rating']) + + volume['post'].create({ + 'context': context, + 'topic': topic, + 'type': 'post', + 'title': {}, + 'message': {}, + 'vote': 1, + }) + self.assertEqual([0, 0], volume['context'][context]['rating']) + self.assertEqual([1, 1], volume['post'][topic]['rating']) + + volume['post'].create({ + 'context': context, + 'topic': topic, + 'type': 'post', + 'title': {}, + 'message': {}, + 'vote': 2, + }) + self.assertEqual([0, 0], volume['context'][context]['rating']) + self.assertEqual([2, 3], volume['post'][topic]['rating']) + if __name__ == '__main__': tests.main() diff --git a/tests/units/model/release.py b/tests/units/model/release.py deleted file mode 100755 index 2f4bfb1..0000000 --- a/tests/units/model/release.py +++ /dev/null @@ -1,124 +0,0 @@ -#!/usr/bin/env python -# sugar-lint: disable - -import os - -import xapian - -from __init__ import tests - -from sugar_network import db -from sugar_network.model import release -from sugar_network.model.release import _fmt_version, Release -from sugar_network.client import IPCConnection -from sugar_network.toolkit import http, coroutine - - -class ReleaseTest(tests.Test): - - def test_fmt_version(self): - self.assertEqual( - xapian.sortable_serialise(eval('1''0000''0000''5''000')), - _fmt_version('1')) - self.assertEqual( - xapian.sortable_serialise(eval('1''0002''0000''5''000')), - _fmt_version('1.2')) - self.assertEqual( - xapian.sortable_serialise(eval('1''0020''0300''5''000')), - _fmt_version('1.20.300')) - self.assertEqual( - xapian.sortable_serialise(eval('1''0020''0300''5''000')), - _fmt_version('1.20.300.4444')) - - self.assertEqual( - xapian.sortable_serialise(eval('1''9999''0000''5''000')), - _fmt_version('10001.99999.10000')) - - self.assertEqual( - xapian.sortable_serialise(eval('1''0000''0000''3''000')), - _fmt_version('1-pre')) - self.assertEqual( - xapian.sortable_serialise(eval('1''0000''0000''4''000')), - _fmt_version('1-rc')) - self.assertEqual( - xapian.sortable_serialise(eval('1''0000''0000''5''000')), - _fmt_version('1-')) - self.assertEqual( - xapian.sortable_serialise(eval('1''0000''0000''6''000')), - _fmt_version('1-r')) - - self.assertEqual( - xapian.sortable_serialise(eval('1''0000''0000''3''001')), - _fmt_version('1-pre1')) - self.assertEqual( - xapian.sortable_serialise(eval('1''0000''0000''4''002')), - _fmt_version('1-rc2')) - self.assertEqual( - xapian.sortable_serialise(eval('1''0000''0000''6''003')), - _fmt_version('1-r3')) - - self.assertEqual( - xapian.sortable_serialise(eval('1''0000''0000''6''000')), - _fmt_version('1-r-2-3')) - self.assertEqual( - xapian.sortable_serialise(eval('1''0000''0000''6''001')), - _fmt_version('1-r1.2-3')) - - def test_OriginalAuthor(self): - self.start_online_client() - client = IPCConnection() - - self.node_volume['context'].create({ - 'guid': 'context', - 'type': 'book', - 'title': 'title', - 'summary': 'summary', - 'description': 'description', - 'author': {'fake': None}, - }) - - guid = client.post(['release'], { - 'context': 'context', - 'license': 'GPLv3+', - 'version': '1', - 'stability': 'stable', - 'notes': '', - }) - self.assertEqual([], self.node_volume['release'].get(guid)['layer']) - - guid = client.post(['release'], { - 'context': 'context', - 'license': 'GPLv3+', - 'version': '1', - 'stability': 'stable', - 'notes': '', - 'layer': ['foo'], - }) - self.assertEqual(['foo'], self.node_volume['release'].get(guid)['layer']) - - self.node_volume['context'].update('context', {'author': {tests.UID: None}}) - - guid = client.post(['release'], { - 'context': 'context', - 'license': 'GPLv3+', - 'version': '1', - 'stability': 'stable', - 'notes': '', - }) - self.assertEqual(['origin'], self.node_volume['release'].get(guid)['layer']) - - guid = client.post(['release'], { - 'context': 'context', - 'license': 'GPLv3+', - 'version': '1', - 'stability': 'stable', - 'notes': '', - 'layer': ['foo'], - }) - self.assertEqual( - sorted(['foo', 'origin']), - sorted(self.node_volume['release'].get(guid)['layer'])) - - -if __name__ == '__main__': - tests.main() diff --git a/tests/units/model/routes.py b/tests/units/model/routes.py index 989dfc1..3c21483 100755 --- a/tests/units/model/routes.py +++ b/tests/units/model/routes.py @@ -10,36 +10,15 @@ from os.path import exists from __init__ import tests, src_root from sugar_network import db, model +from sugar_network.db import files from sugar_network.model.user import User from sugar_network.toolkit.router import Router, Request +from sugar_network.toolkit.coroutine import this from sugar_network.toolkit import coroutine class RoutesTest(tests.Test): - def test_StaticFiles(self): - router = Router(model.FrontRoutes()) - local_path = src_root + '/sugar_network/static/httpdocs/images/missing.png' - - response = [] - reply = router({ - 'PATH_INFO': '/static/images/missing.png', - 'REQUEST_METHOD': 'GET', - }, - lambda status, headers: response.extend([status, dict(headers)])) - result = file(local_path).read() - self.assertEqual(result, ''.join([i for i in reply])) - self.assertEqual([ - '200 OK', - { - 'last-modified': formatdate(os.stat(local_path).st_mtime, localtime=False, usegmt=True), - 'content-length': str(len(result)), - 'content-type': 'image/png', - 'content-disposition': 'attachment; filename="missing.png"', - } - ], - response) - def test_Subscribe(self): class Document(db.Resource): @@ -49,7 +28,7 @@ class RoutesTest(tests.Test): return value routes = model.FrontRoutes() - volume = db.Volume('db', [Document], routes.broadcast) + volume = db.Volume('db', [Document]) events = [] def read_events(): @@ -69,6 +48,7 @@ class RoutesTest(tests.Test): job.kill() self.assertEqual([ + {'event': 'pong'}, {'guid': 'guid', 'resource': 'document', 'event': 'create'}, {'guid': 'guid', 'resource': 'document', 'event': 'update'}, {'guid': 'guid', 'event': 'delete', 'resource': u'document'}, @@ -77,66 +57,10 @@ class RoutesTest(tests.Test): def test_SubscribeWithPong(self): routes = model.FrontRoutes() - for event in routes.subscribe(ping=True): + for event in routes.subscribe(): break self.assertEqual({'event': 'pong'}, event) - def test_feed(self): - volume = db.Volume('db', model.RESOURCES) - routes = model.VolumeRoutes(volume) - - volume['context'].create({ - 'guid': 'context', - 'type': 'activity', - 'title': '', - 'summary': '', - 'description': '', - 'dependencies': ['foo', 'bar'], - }) - volume['release'].create({ - 'guid': 'release', - 'context': 'context', - 'license': 'GPLv3', - 'version': '1', - 'date': 0, - 'stability': 'stable', - 'notes': '', - 'data': { - 'spec': { - '*-*': { - 'commands': {'activity': {'exec': 'true'}}, - 'requires': {'dep': {}, 'sugar': {'restrictions': [['0.88', None]]}}, - }, - }, - }, - }) - - self.assertEqual({ - 'releases': [ - { - 'guid': 'release', - 'author': {}, - 'ctime': 0, - 'data': { - 'spec': { - '*-*': { - 'commands': {'activity': {'exec': 'true'}}, - 'requires': {'dep': {}, 'sugar': {'restrictions': [['0.88', None]]}}, - }, - }, - }, - 'layer': [], - 'license': 'GPLv3', - 'notes': {'en-us': ''}, - 'stability': 'stable', - 'tags': [], - 'version': '1', - 'requires': {'bar': {}, 'foo': {}}, - }, - ], - }, - routes.feed(Request(method='GET', path=['context', 'context']), 'foo')) - if __name__ == '__main__': tests.main() diff --git a/tests/units/node/__main__.py b/tests/units/node/__main__.py index ac37315..dfadaf3 100644 --- a/tests/units/node/__main__.py +++ b/tests/units/node/__main__.py @@ -4,16 +4,14 @@ from __init__ import tests from downloads import * from files import * -from master import * from node import * from obs import * -from stats_node import * from stats_user import * from sync import * from sync_master import * from sync_offline import * from sync_online import * -from volume import * +from model import * if __name__ == '__main__': tests.main() diff --git a/tests/units/node/master.py b/tests/units/node/master.py deleted file mode 100755 index b3eaa75..0000000 --- a/tests/units/node/master.py +++ /dev/null @@ -1,214 +0,0 @@ -#!/usr/bin/env python -# sugar-lint: disable - -import os - -from __init__ import tests - -from sugar_network.node import obs -from sugar_network.client import IPCConnection -from sugar_network.toolkit import coroutine, enforce - - -class MasterTest(tests.Test): - - def test_Aliases(self): - self.override(obs, 'get_repos', lambda: [ - {'distributor_id': 'Gentoo', 'name': 'Gentoo-2.1', 'arches': ['x86', 'x86_64']}, - {'distributor_id': 'Debian', 'name': 'Debian-6.0', 'arches': ['x86']}, - {'distributor_id': 'Debian', 'name': 'Debian-7.0', 'arches': ['x86_64']}, - ]) - self.override(obs, 'resolve', lambda repo, arch, names: ['fake']) - - self.start_online_client() - ipc = IPCConnection() - - guid = ipc.post(['context'], { - 'type': 'activity', - 'title': 'title', - 'summary': 'summary', - 'description': 'description', - }) - - ipc.put(['context', guid, 'aliases'], { - 'Gentoo': { - 'binary': [['pkg1.bin', 'pkg2.bin']], - 'devel': [['pkg3.devel']], - }, - 'Debian': { - 'binary': [['pkg4.bin']], - 'devel': [['pkg5.devel', 'pkg6.devel']], - }, - }) - coroutine.dispatch() - self.assertEqual({ - 'Gentoo-2.1': {'status': 'success', 'binary': ['pkg1.bin', 'pkg2.bin'], 'devel': ['pkg3.devel']}, - 'Debian-6.0': {'status': 'success', 'binary': ['pkg4.bin'], 'devel': ['pkg5.devel', 'pkg6.devel']}, - 'Debian-7.0': {'status': 'success', 'binary': ['pkg4.bin'], 'devel': ['pkg5.devel', 'pkg6.devel']}, - }, - ipc.get(['context', guid, 'packages'])) - - def test_WrongAliases(self): - self.override(obs, 'get_repos', lambda: [ - {'distributor_id': 'Gentoo', 'name': 'Gentoo-2.1', 'arches': ['x86', 'x86_64']}, - {'distributor_id': 'Debian', 'name': 'Debian-6.0', 'arches': ['x86']}, - {'distributor_id': 'Debian', 'name': 'Debian-7.0', 'arches': ['x86_64']}, - ]) - self.override(obs, 'resolve', lambda repo, arch, names: enforce(False, 'resolve failed')) - - self.start_online_client() - ipc = IPCConnection() - - guid = ipc.post(['context'], { - 'type': 'activity', - 'title': 'title', - 'summary': 'summary', - 'description': 'description', - }) - - ipc.put(['context', guid, 'aliases'], { - 'Gentoo': { - 'binary': [['pkg1.bin', 'pkg2.bin']], - 'devel': [['pkg3.devel']], - }, - 'Debian': { - 'binary': [['pkg4.bin']], - 'devel': [['pkg5.devel', 'pkg6.devel']], - }, - }) - coroutine.dispatch() - self.assertEqual({ - 'Gentoo-2.1': {'status': 'resolve failed'}, - 'Debian-6.0': {'status': 'resolve failed'}, - 'Debian-7.0': {'status': 'resolve failed'}, - }, - ipc.get(['context', guid, 'packages'])) - - def test_MultipleAliases(self): - - def resolve(repo, arch, names): - enforce(not [i for i in names if 'fake' in i], 'resolve failed') - return ['fake'] - - self.override(obs, 'get_repos', lambda: [ - {'distributor_id': 'Gentoo', 'name': 'Gentoo-2.1', 'arches': ['x86', 'x86_64']}, - ]) - self.override(obs, 'resolve', resolve) - - self.start_online_client() - ipc = IPCConnection() - - guid = ipc.post(['context'], { - 'type': 'activity', - 'title': 'title', - 'summary': 'summary', - 'description': 'description', - }) - - ipc.put(['context', guid, 'aliases'], { - 'Gentoo': { - 'binary': [['fake.bin'], ['proper.bin'], ['not-reach.bin']], - 'devel': [['fake.devel'], ['proper.devel'], ['not-reach.devel']], - }, - }) - coroutine.dispatch() - self.assertEqual({ - 'Gentoo-2.1': {'status': 'success', 'binary': ['proper.bin'], 'devel': ['proper.devel']}, - }, - ipc.get(['context', guid, 'packages'])) - - ipc.put(['context', guid, 'aliases'], { - 'Gentoo': { - 'binary': [['proper.bin']], - 'devel': [['fake.devel']], - }, - }) - coroutine.dispatch() - self.assertEqual({ - 'Gentoo-2.1': {'status': 'resolve failed', 'binary': ['proper.bin']}, - }, - ipc.get(['context', guid, 'packages'])) - - ipc.put(['context', guid, 'aliases'], { - 'Gentoo': { - 'binary': [['fake.bin']], - 'devel': [['proper.devel']], - }, - }) - coroutine.dispatch() - self.assertEqual({ - 'Gentoo-2.1': {'status': 'resolve failed'}, - }, - ipc.get(['context', guid, 'packages'])) - - def test_InvalidateSolutions(self): - self.override(obs, 'get_repos', lambda: [ - {'distributor_id': 'Gentoo', 'name': 'Gentoo-2.1', 'arches': ['x86_64']}, - ]) - self.override(obs, 'resolve', lambda repo, arch, names: ['fake']) - - self.start_online_client() - ipc = IPCConnection() - - events = [] - def read_events(): - for event in ipc.subscribe(): - if event.get('resource') == 'release': - events.append(event) - job = coroutine.spawn(read_events) - - guid = ipc.post(['context'], { - 'type': 'activity', - 'title': 'title', - 'summary': 'summary', - 'description': 'description', - }) - coroutine.sleep(.5) - self.assertEqual([], events) - - ipc.put(['context', guid, 'aliases'], { - 'Gentoo': { - 'binary': [['bin']], - 'devel': [['devel']], - }, - }) - coroutine.sleep(.5) - self.assertEqual([ - {'event': 'populate', 'resource': 'release', 'mtime': int(os.stat('master/release/index/mtime').st_mtime)}, - ], - events) - self.assertEqual({ - 'Gentoo-2.1': {'status': 'success', 'binary': ['bin'], 'devel': ['devel']}, - }, - ipc.get(['context', guid, 'packages'])) - - def test_InvalidateSolutionsOnDependenciesChanges(self): - self.start_online_client() - ipc = IPCConnection() - - events = [] - def read_events(): - for event in ipc.subscribe(): - if event.get('resource') == 'release': - events.append(event) - job = coroutine.spawn(read_events) - - guid = ipc.post(['context'], { - 'type': 'activity', - 'title': 'title', - 'summary': 'summary', - 'description': 'description', - 'dependencies': [], - }) - self.assertEqual(0, len(events)) - - ipc.put(['context', guid, 'dependencies'], ['foo']) - coroutine.sleep(.1) - self.assertEqual([ - {'event': 'populate', 'resource': 'release', 'mtime': int(os.stat('master/release/index/mtime').st_mtime)}, - ], - events) - - -if __name__ == '__main__': - tests.main() diff --git a/tests/units/node/model.py b/tests/units/node/model.py new file mode 100755 index 0000000..68215c1 --- /dev/null +++ b/tests/units/node/model.py @@ -0,0 +1,660 @@ +#!/usr/bin/env python +# sugar-lint: disable + +import os +import time + +from __init__ import tests + +from sugar_network import db, toolkit +from sugar_network.db import files +from sugar_network.client import Connection, keyfile, api_url +from sugar_network.model.user import User +from sugar_network.model.post import Post +from sugar_network.node import model, obs +from sugar_network.node.routes import NodeRoutes +from sugar_network.toolkit.coroutine import this +from sugar_network.toolkit.router import Request, Router +from sugar_network.toolkit import i18n, http, coroutine, enforce + + +class ModelTest(tests.Test): + + def test_IncrementReleasesSeqno(self): + events = [] + volume = self.start_master([User, model.Context, Post]) + this.broadcast = lambda x: events.append(x) + conn = Connection(auth=http.SugarAuth(keyfile.value)) + + context = conn.post(['context'], { + 'type': 'group', + 'title': 'Activity', + 'summary': 'summary', + 'description': 'description', + }) + self.assertEqual([ + ], [i for i in events if i['event'] == 'release']) + self.assertEqual(0, volume.releases_seqno.value) + + aggid = conn.post(['context', context, 'releases'], -1) + self.assertEqual([ + {'event': 'release', 'seqno': 1}, + ], [i for i in events if i['event'] == 'release']) + self.assertEqual(1, volume.releases_seqno.value) + + def test_diff(self): + self.override(time, 'time', lambda: 0) + self.override(NodeRoutes, 'authorize', lambda self, user, role: True) + + class Document(db.Resource): + + @db.indexed_property(slot=1) + def prop(self, value): + return value + + volume = self.start_master([User, Document]) + conn = Connection(auth=http.SugarAuth(keyfile.value)) + + guid1 = conn.post(['document'], {'prop': 'a'}) + self.utime('master/document/%s/%s' % (guid1[:2], guid1), 1) + guid2 = conn.post(['document'], {'prop': 'b'}) + self.utime('master/document/%s/%s' % (guid2[:2], guid2), 2) + + in_seq = toolkit.Sequence([[1, None]]) + self.assertEqual([ + {'resource': 'document'}, + {'guid': guid1, + 'diff': { + 'guid': {'value': guid1, 'mtime': 1}, + 'mtime': {'value': 0, 'mtime': 1}, + 'ctime': {'value': 0, 'mtime': 1}, + 'prop': {'value': 'a', 'mtime': 1}, + 'author': {'mtime': 1, 'value': {}}, + 'layer': {'mtime': 1, 'value': []}, + 'tags': {'mtime': 1, 'value': []}, + }, + }, + {'guid': guid2, + 'diff': { + 'guid': {'value': guid2, 'mtime': 2}, + 'mtime': {'value': 0, 'mtime': 2}, + 'ctime': {'value': 0, 'mtime': 2}, + 'prop': {'value': 'b', 'mtime': 2}, + 'author': {'mtime': 2, 'value': {}}, + 'layer': {'mtime': 2, 'value': []}, + 'tags': {'mtime': 2, 'value': []}, + }, + }, + {'commit': [[1, 2]]}, + ], + [i for i in model.diff(volume, in_seq)]) + self.assertEqual([[1, None]], in_seq) + + def test_diff_Partial(self): + self.override(time, 'time', lambda: 0) + self.override(NodeRoutes, 'authorize', lambda self, user, role: True) + + class Document(db.Resource): + + @db.indexed_property(slot=1) + def prop(self, value): + return value + + volume = self.start_master([User, Document]) + conn = Connection(auth=http.SugarAuth(keyfile.value)) + + guid1 = conn.post(['document'], {'prop': 'a'}) + self.utime('master/document/%s/%s' % (guid1[:2], guid1), 1) + guid2 = conn.post(['document'], {'prop': 'b'}) + self.utime('master/document/%s/%s' % (guid2[:2], guid2), 2) + + in_seq = toolkit.Sequence([[1, None]]) + patch = model.diff(volume, in_seq) + self.assertEqual({'resource': 'document'}, next(patch)) + self.assertEqual(guid1, next(patch)['guid']) + self.assertEqual({'commit': []}, patch.throw(StopIteration())) + try: + next(patch) + assert False + except StopIteration: + pass + + patch = model.diff(volume, in_seq) + self.assertEqual({'resource': 'document'}, next(patch)) + self.assertEqual(guid1, next(patch)['guid']) + self.assertEqual(guid2, next(patch)['guid']) + self.assertEqual({'commit': [[1, 1]]}, patch.throw(StopIteration())) + try: + next(patch) + assert False + except StopIteration: + pass + + def test_diff_Stretch(self): + self.override(time, 'time', lambda: 0) + self.override(NodeRoutes, 'authorize', lambda self, user, role: True) + + class Document(db.Resource): + + @db.indexed_property(slot=1) + def prop(self, value): + return value + + volume = self.start_master([User, Document]) + conn = Connection(auth=http.SugarAuth(keyfile.value)) + + guid1 = conn.post(['document'], {'prop': 'a'}) + self.utime('master/document/%s/%s' % (guid1[:2], guid1), 1) + guid2 = conn.post(['document'], {'prop': 'b'}) + volume['document'].delete(guid2) + guid3 = conn.post(['document'], {'prop': 'c'}) + self.utime('master/document/%s/%s' % (guid3[:2], guid3), 2) + guid4 = conn.post(['document'], {'prop': 'd'}) + volume['document'].delete(guid4) + guid5 = conn.post(['document'], {'prop': 'f'}) + self.utime('master/document/%s/%s' % (guid5[:2], guid5), 2) + + in_seq = toolkit.Sequence([[1, None]]) + patch = model.diff(volume, in_seq) + self.assertEqual({'resource': 'document'}, patch.send(None)) + self.assertEqual(guid1, patch.send(None)['guid']) + self.assertEqual(guid3, patch.send(None)['guid']) + self.assertEqual(guid5, patch.send(None)['guid']) + self.assertEqual({'commit': [[1, 1], [3, 3]]}, patch.throw(StopIteration())) + try: + patch.send(None) + assert False + except StopIteration: + pass + + patch = model.diff(volume, in_seq) + self.assertEqual({'resource': 'document'}, patch.send(None)) + self.assertEqual(guid1, patch.send(None)['guid']) + self.assertEqual(guid3, patch.send(None)['guid']) + self.assertEqual(guid5, patch.send(None)['guid']) + self.assertEqual({'commit': [[1, 5]]}, patch.send(None)) + try: + patch.send(None) + assert False + except StopIteration: + pass + + def test_diff_DoNotStretchContinuesPacket(self): + self.override(time, 'time', lambda: 0) + self.override(NodeRoutes, 'authorize', lambda self, user, role: True) + + class Document(db.Resource): + + @db.indexed_property(slot=1) + def prop(self, value): + return value + + volume = self.start_master([User, Document]) + conn = Connection(auth=http.SugarAuth(keyfile.value)) + + guid1 = conn.post(['document'], {'prop': 'a'}) + volume['document'].delete(guid1) + guid2 = conn.post(['document'], {'prop': 'b'}) + volume['document'].delete(guid2) + guid3 = conn.post(['document'], {'prop': 'c'}) + self.utime('master/document/%s/%s' % (guid3[:2], guid3), 2) + guid4 = conn.post(['document'], {'prop': 'd'}) + volume['document'].delete(guid4) + guid5 = conn.post(['document'], {'prop': 'f'}) + self.utime('master/document/%s/%s' % (guid5[:2], guid5), 2) + + in_seq = toolkit.Sequence([[1, None]]) + patch = model.diff(volume, in_seq, toolkit.Sequence([[1, 1]])) + self.assertEqual({'resource': 'document'}, patch.send(None)) + self.assertEqual(guid3, patch.send(None)['guid']) + self.assertEqual(guid5, patch.send(None)['guid']) + self.assertEqual({'commit': [[1, 1], [3, 3], [5, 5]]}, patch.send(None)) + try: + patch.send(None) + assert False + except StopIteration: + pass + + def test_diff_TheSameInSeqForAllDocuments(self): + self.override(time, 'time', lambda: 0) + self.override(NodeRoutes, 'authorize', lambda self, user, role: True) + + class Document1(db.Resource): + pass + + class Document2(db.Resource): + pass + + class Document3(db.Resource): + pass + + volume = self.start_master([User, Document1, Document2, Document3]) + conn = Connection(auth=http.SugarAuth(keyfile.value)) + + guid3 = conn.post(['document1'], {}) + self.utime('master/document/%s/%s' % (guid3[:2], guid3), 3) + guid2 = conn.post(['document2'], {}) + self.utime('master/document/%s/%s' % (guid2[:2], guid2), 2) + guid1 = conn.post(['document3'], {}) + self.utime('master/document/%s/%s' % (guid1[:2], guid1), 1) + + in_seq = toolkit.Sequence([[1, None]]) + patch = model.diff(volume, in_seq) + self.assertEqual({'resource': 'document1'}, patch.send(None)) + self.assertEqual(guid3, patch.send(None)['guid']) + self.assertEqual({'resource': 'document2'}, patch.send(None)) + self.assertEqual(guid2, patch.send(None)['guid']) + self.assertEqual({'resource': 'document3'}, patch.send(None)) + self.assertEqual(guid1, patch.send(None)['guid']) + self.assertEqual({'commit': [[1, 3]]}, patch.send(None)) + try: + patch.send(None) + assert False + except StopIteration: + pass + + def test_merge_Create(self): + self.override(time, 'time', lambda: 0) + self.override(NodeRoutes, 'authorize', lambda self, user, role: True) + + class Document1(db.Resource): + + @db.indexed_property(slot=1) + def prop(self, value): + return value + + class Document2(db.Resource): + pass + + self.touch(('master/db.seqno', '100')) + volume = self.start_master([Document1, Document2]) + + records = [ + {'resource': 'document1'}, + {'guid': '1', 'diff': { + 'guid': {'value': '1', 'mtime': 1.0}, + 'ctime': {'value': 2, 'mtime': 2.0}, + 'mtime': {'value': 3, 'mtime': 3.0}, + 'prop': {'value': '4', 'mtime': 4.0}, + }}, + {'resource': 'document2'}, + {'guid': '5', 'diff': { + 'guid': {'value': '5', 'mtime': 5.0}, + 'ctime': {'value': 6, 'mtime': 6.0}, + 'mtime': {'value': 7, 'mtime': 7.0}, + }}, + {'commit': [[1, 2]]}, + ] + self.assertEqual(([[1, 2]], [[101, 102]]), model.merge(volume, records)) + + self.assertEqual( + {'guid': '1', 'prop': '4', 'ctime': 2, 'mtime': 3}, + volume['document1'].get('1').properties(['guid', 'ctime', 'mtime', 'prop'])) + self.assertEqual(1, os.stat('master/document1/1/1/guid').st_mtime) + self.assertEqual(2, os.stat('master/document1/1/1/ctime').st_mtime) + self.assertEqual(3, os.stat('master/document1/1/1/mtime').st_mtime) + self.assertEqual(4, os.stat('master/document1/1/1/prop').st_mtime) + + self.assertEqual( + {'guid': '5', 'ctime': 6, 'mtime': 7}, + volume['document2'].get('5').properties(['guid', 'ctime', 'mtime'])) + self.assertEqual(5, os.stat('master/document2/5/5/guid').st_mtime) + self.assertEqual(6, os.stat('master/document2/5/5/ctime').st_mtime) + self.assertEqual(7, os.stat('master/document2/5/5/mtime').st_mtime) + + def test_merge_Update(self): + self.override(time, 'time', lambda: 0) + self.override(NodeRoutes, 'authorize', lambda self, user, role: True) + + class Document(db.Resource): + + @db.indexed_property(slot=1) + def prop(self, value): + return value + + self.touch(('master/db.seqno', '100')) + volume = db.Volume('master', [Document]) + volume['document'].create({'guid': '1', 'prop': '1', 'ctime': 1, 'mtime': 1}) + for i in os.listdir('master/document/1/1'): + os.utime('master/document/1/1/%s' % i, (2, 2)) + + records = [ + {'resource': 'document'}, + {'guid': '1', 'diff': {'prop': {'value': '2', 'mtime': 1.0}}}, + {'commit': [[1, 1]]}, + ] + self.assertEqual(([[1, 1]], []), model.merge(volume, records)) + self.assertEqual( + {'guid': '1', 'prop': '1', 'ctime': 1, 'mtime': 1}, + volume['document'].get('1').properties(['guid', 'ctime', 'mtime', 'prop'])) + self.assertEqual(2, os.stat('master/document/1/1/prop').st_mtime) + + records = [ + {'resource': 'document'}, + {'guid': '1', 'diff': {'prop': {'value': '3', 'mtime': 2.0}}}, + {'commit': [[2, 2]]}, + ] + self.assertEqual(([[2, 2]], []), model.merge(volume, records)) + self.assertEqual( + {'guid': '1', 'prop': '1', 'ctime': 1, 'mtime': 1}, + volume['document'].get('1').properties(['guid', 'ctime', 'mtime', 'prop'])) + self.assertEqual(2, os.stat('master/document/1/1/prop').st_mtime) + + records = [ + {'resource': 'document'}, + {'guid': '1', 'diff': {'prop': {'value': '4', 'mtime': 3.0}}}, + {'commit': [[3, 3]]}, + ] + self.assertEqual(([[3, 3]], [[102, 102]]), model.merge(volume, records)) + self.assertEqual( + {'guid': '1', 'prop': '4', 'ctime': 1, 'mtime': 1}, + volume['document'].get('1').properties(['guid', 'ctime', 'mtime', 'prop'])) + self.assertEqual(3, os.stat('master/document/1/1/prop').st_mtime) + + def test_merge_MultipleCommits(self): + self.override(time, 'time', lambda: 0) + + class Document(db.Resource): + + @db.stored_property() + def prop(self, value): + return value + + self.touch(('master/db.seqno', '100')) + volume = db.Volume('master', [Document]) + + def generator(): + for i in [ + {'resource': 'document'}, + {'commit': [[1, 1]]}, + {'guid': '1', 'diff': { + 'guid': {'value': '1', 'mtime': 1.0}, + 'ctime': {'value': 2, 'mtime': 2.0}, + 'mtime': {'value': 3, 'mtime': 3.0}, + 'prop': {'value': '4', 'mtime': 4.0}, + }}, + {'commit': [[2, 3]]}, + ]: + yield i + + records = generator() + self.assertEqual(([[1, 3]], [[101, 101]]), model.merge(volume, records)) + assert volume['document'].exists('1') + + def test_diff_ByLayers(self): + self.override(time, 'time', lambda: 0) + self.override(NodeRoutes, 'authorize', lambda self, user, role: True) + + class Context(db.Resource): + pass + + class Post(db.Resource): + pass + + this.request = Request() + volume = db.Volume('db', [Context, Post]) + volume['context'].create({'guid': '0', 'ctime': 1, 'mtime': 1, 'layer': ['layer0', 'common']}) + volume['context'].create({'guid': '1', 'ctime': 1, 'mtime': 1, 'layer': ['layer1']}) + volume['post'].create({'guid': '3', 'ctime': 3, 'mtime': 3, 'layer': 'layer3'}) + + volume['context'].update('0', {'tags': '0'}) + volume['context'].update('1', {'tags': '1'}) + volume['post'].update('3', {'tags': '3'}) + self.utime('db', 0) + + self.assertEqual(sorted([ + {'resource': 'context'}, + {'guid': '0', 'diff': {'tags': {'value': '0', 'mtime': 0}}}, + {'guid': '1', 'diff': {'tags': {'value': '1', 'mtime': 0}}}, + {'resource': 'post'}, + {'guid': '3', 'diff': {'tags': {'value': '3', 'mtime': 0}}}, + {'commit': [[4, 6]]}, + ]), + sorted([i for i in model.diff(volume, toolkit.Sequence([[4, None]]))])) + + self.assertEqual(sorted([ + {'resource': 'context'}, + {'guid': '0', 'diff': {'tags': {'value': '0', 'mtime': 0}}}, + {'guid': '1', 'diff': {'tags': {'value': '1', 'mtime': 0}}}, + {'resource': 'post'}, + {'guid': '3', 'diff': {'tags': {'value': '3', 'mtime': 0}}}, + {'commit': [[4, 6]]}, + ]), + sorted([i for i in model.diff(volume, toolkit.Sequence([[4, None]]), layer='layer1')])) + + self.assertEqual(sorted([ + {'resource': 'context'}, + {'guid': '0', 'diff': {'tags': {'value': '0', 'mtime': 0}}}, + {'resource': 'post'}, + {'guid': '3', 'diff': {'tags': {'value': '3', 'mtime': 0}}}, + {'commit': [[4, 6]]}, + ]), + sorted([i for i in model.diff(volume, toolkit.Sequence([[4, None]]), layer='layer2')])) + + self.assertEqual(sorted([ + {'resource': 'context'}, + {'guid': '0', 'diff': {'tags': {'value': '0', 'mtime': 0}}}, + {'resource': 'post'}, + {'guid': '3', 'diff': {'tags': {'value': '3', 'mtime': 0}}}, + {'commit': [[4, 6]]}, + ]), + sorted([i for i in model.diff(volume, toolkit.Sequence([[4, None]]), layer='foo')])) + + def test_Packages(self): + self.override(obs, 'get_repos', lambda: [ + {'lsb_id': 'Gentoo', 'lsb_release': '2.1', 'name': 'Gentoo-2.1', 'arches': ['x86', 'x86_64']}, + {'lsb_id': 'Debian', 'lsb_release': '6.0', 'name': 'Debian-6.0', 'arches': ['x86']}, + {'lsb_id': 'Debian', 'lsb_release': '7.0', 'name': 'Debian-7.0', 'arches': ['x86_64']}, + ]) + self.override(obs, 'resolve', lambda repo, arch, names: ['fake']) + + volume = self.start_master([User, model.Context]) + conn = http.Connection(api_url.value, http.SugarAuth(keyfile.value)) + + guid = conn.post(['context'], { + 'type': 'package', + 'title': 'title', + 'summary': 'summary', + 'description': 'description', + }) + conn.put(['context', guid, 'releases', '*'], { + 'binary': ['pkg1.bin', 'pkg2.bin'], + 'devel': 'pkg3.devel', + }) + self.assertEqual({ + '*': { + 'seqno': 3, + 'author': {tests.UID: {'name': tests.UID, 'order': 0, 'role': 3}}, + 'value': {'binary': ['pkg1.bin', 'pkg2.bin'], 'devel': ['pkg3.devel']}, + }, + 'status': { + 'Gentoo-2.1': 'success', + 'Debian-6.0': 'success', + 'Debian-7.0': 'success', + }, + }, + volume['context'][guid]['releases']) + + guid = conn.post(['context'], { + 'type': 'package', + 'title': 'title', + 'summary': 'summary', + 'description': 'description', + }) + conn.put(['context', guid, 'releases', 'Gentoo'], { + 'binary': ['pkg1.bin', 'pkg2.bin'], + 'devel': 'pkg3.devel', + }) + self.assertEqual({ + 'Gentoo': { + 'seqno': 5, + 'author': {tests.UID: {'name': tests.UID, 'order': 0, 'role': 3}}, + 'value': {'binary': ['pkg1.bin', 'pkg2.bin'], 'devel': ['pkg3.devel']}, + }, + 'status': { + 'Gentoo-2.1': 'success', + }, + }, + volume['context'][guid]['releases']) + + guid = conn.post(['context'], { + 'type': 'package', + 'title': 'title', + 'summary': 'summary', + 'description': 'description', + }) + conn.put(['context', guid, 'releases', 'Debian-6.0'], { + 'binary': ['pkg1.bin', 'pkg2.bin'], + 'devel': 'pkg3.devel', + }) + self.assertEqual({ + 'Debian-6.0': { + 'seqno': 7, + 'author': {tests.UID: {'name': tests.UID, 'order': 0, 'role': 3}}, + 'value': {'binary': ['pkg1.bin', 'pkg2.bin'], 'devel': ['pkg3.devel']}, + }, + 'status': { + 'Debian-6.0': 'success', + }, + }, + volume['context'][guid]['releases']) + + def test_UnresolvedPackages(self): + self.override(obs, 'get_repos', lambda: [ + {'lsb_id': 'Gentoo', 'lsb_release': '2.1', 'name': 'Gentoo-2.1', 'arches': ['x86', 'x86_64']}, + ]) + self.override(obs, 'resolve', lambda repo, arch, names: enforce(False, 'resolve failed')) + + volume = self.start_master([User, model.Context]) + conn = http.Connection(api_url.value, http.SugarAuth(keyfile.value)) + + guid = conn.post(['context'], { + 'type': 'package', + 'title': 'title', + 'summary': 'summary', + 'description': 'description', + }) + conn.put(['context', guid, 'releases', '*'], { + 'binary': ['pkg1.bin', 'pkg2.bin'], + 'devel': 'pkg3.devel', + }) + self.assertEqual({ + '*': { + 'seqno': 3, + 'author': {tests.UID: {'name': tests.UID, 'order': 0, 'role': 3}}, + 'value': {'binary': ['pkg1.bin', 'pkg2.bin'], 'devel': ['pkg3.devel']}, + }, + 'status': { + 'Gentoo-2.1': 'resolve failed', + }, + }, + volume['context'][guid]['releases']) + + def test_PackageOverrides(self): + self.override(obs, 'get_repos', lambda: [ + {'lsb_id': 'Gentoo', 'lsb_release': '2.1', 'name': 'Gentoo-2.1', 'arches': ['x86', 'x86_64']}, + {'lsb_id': 'Debian', 'lsb_release': '6.0', 'name': 'Debian-6.0', 'arches': ['x86']}, + {'lsb_id': 'Debian', 'lsb_release': '7.0', 'name': 'Debian-7.0', 'arches': ['x86_64']}, + ]) + + volume = self.start_master([User, model.Context]) + conn = http.Connection(api_url.value, http.SugarAuth(keyfile.value)) + guid = conn.post(['context'], { + 'type': 'package', + 'title': 'title', + 'summary': 'summary', + 'description': 'description', + }) + + self.override(obs, 'resolve', lambda repo, arch, names: enforce(False, '1')) + conn.put(['context', guid, 'releases', '*'], {'binary': '1'}) + self.assertEqual({ + '*': { + 'seqno': 3, + 'author': {tests.UID: {'name': tests.UID, 'order': 0, 'role': 3}}, + 'value': {'binary': ['1']}, + }, + 'status': { + 'Gentoo-2.1': '1', + 'Debian-6.0': '1', + 'Debian-7.0': '1', + }, + }, + volume['context'][guid]['releases']) + + self.override(obs, 'resolve', lambda repo, arch, names: enforce(False, '2')) + conn.put(['context', guid, 'releases', 'Debian'], {'binary': '2'}) + self.assertEqual({ + '*': { + 'seqno': 3, + 'author': {tests.UID: {'name': tests.UID, 'order': 0, 'role': 3}}, + 'value': {'binary': ['1']}, + }, + 'Debian': { + 'seqno': 4, + 'author': {tests.UID: {'name': tests.UID, 'order': 0, 'role': 3}}, + 'value': {'binary': ['2']}, + }, + 'status': { + 'Gentoo-2.1': '1', + 'Debian-6.0': '2', + 'Debian-7.0': '2', + }, + }, + volume['context'][guid]['releases']) + + self.override(obs, 'resolve', lambda repo, arch, names: enforce(False, '3')) + conn.put(['context', guid, 'releases', 'Debian-6.0'], {'binary': '3'}) + self.assertEqual({ + '*': { + 'seqno': 3, + 'author': {tests.UID: {'name': tests.UID, 'order': 0, 'role': 3}}, + 'value': {'binary': ['1']}, + }, + 'Debian': { + 'seqno': 4, + 'author': {tests.UID: {'name': tests.UID, 'order': 0, 'role': 3}}, + 'value': {'binary': ['2']}, + }, + 'Debian-6.0': { + 'seqno': 5, + 'author': {tests.UID: {'name': tests.UID, 'order': 0, 'role': 3}}, + 'value': {'binary': ['3']}, + }, + 'status': { + 'Gentoo-2.1': '1', + 'Debian-6.0': '3', + 'Debian-7.0': '2', + }, + }, + volume['context'][guid]['releases']) + + self.override(obs, 'resolve', lambda repo, arch, names: enforce(False, '4')) + conn.put(['context', guid, 'releases', 'Debian'], {'binary': '4'}) + self.assertEqual({ + '*': { + 'seqno': 3, + 'author': {tests.UID: {'name': tests.UID, 'order': 0, 'role': 3}}, + 'value': {'binary': ['1']}, + }, + 'Debian': { + 'seqno': 6, + 'author': {tests.UID: {'name': tests.UID, 'order': 0, 'role': 3}}, + 'value': {'binary': ['4']}, + }, + 'Debian-6.0': { + 'seqno': 5, + 'author': {tests.UID: {'name': tests.UID, 'order': 0, 'role': 3}}, + 'value': {'binary': ['3']}, + }, + 'status': { + 'Gentoo-2.1': '1', + 'Debian-6.0': '3', + 'Debian-7.0': '4', + }, + }, + volume['context'][guid]['releases']) + + +if __name__ == '__main__': + tests.main() diff --git a/tests/units/node/node.py b/tests/units/node/node.py index d8f00ec..0058918 100755 --- a/tests/units/node/node.py +++ b/tests/units/node/node.py @@ -16,17 +16,17 @@ from os.path import exists, join from __init__ import tests from sugar_network import db, node, model, client -from sugar_network.client import Connection, keyfile +from sugar_network.db import files +from sugar_network.client import Connection, keyfile, api_url from sugar_network.toolkit import http, coroutine from sugar_network.toolkit.rrd import Rrd -from sugar_network.node import stats_user, stats_node, obs -from sugar_network.node.routes import NodeRoutes, generate_node_stats +from sugar_network.node import stats_user +from sugar_network.node.routes import NodeRoutes from sugar_network.node.master import MasterRoutes from sugar_network.model.user import User from sugar_network.model.context import Context -from sugar_network.model.release import Release from sugar_network.model.user import User -from sugar_network.toolkit.router import Router, Request, Response, fallbackroute, Blob, ACL, route +from sugar_network.toolkit.router import Router, Request, Response, fallbackroute, ACL, route from sugar_network.toolkit import http @@ -40,7 +40,7 @@ class NodeTest(tests.Test): def test_UserStats(self): volume = db.Volume('db', model.RESOURCES) - cp = NodeRoutes('guid', volume) + cp = NodeRoutes('guid', volume=volume) call(cp, method='POST', document='user', principal=tests.UID, content={ 'name': 'user', @@ -100,69 +100,10 @@ class NodeTest(tests.Test): }, call(cp, method='GET', cmd='stats-info', document='user', guid=tests.UID, principal=tests.UID)) - def test_NodeStats(self): - stats_node.stats_node.value = True - stats_node.stats_node_rras.value = ['RRA:AVERAGE:0.5:1:60', 'RRA:AVERAGE:0.5:3:60'] - rrd = Rrd('stats/node', stats_node.stats_node_step.value, stats_node.stats_node_rras.value) - - ts = int(time.time()) / 3 * 3 - for i in range(10): - rrd['user'].put({'total': i}, ts + i) - - volume = db.Volume('db', model.RESOURCES) - cp = NodeRoutes('guid', volume) - - self.assertEqual({ - 'user': [ - (ts + 0, {'total': 0.0}), - (ts + 1, {'total': 1.0}), - (ts + 2, {'total': 2.0}), - (ts + 3, {'total': 3.0}), - ], - }, - call(cp, method='GET', cmd='stats', source='user.total', start=ts, end=ts + 3, records=4)) - - self.assertEqual({ - 'user': [ - (ts + 0, {'total': 0.0}), - (ts + 3, {'total': 2.0}), - (ts + 6, {'total': 5.0}), - (ts + 9, {'total': 8.0}), - ], - }, - call(cp, method='GET', cmd='stats', source='user.total', start=ts, end=ts + 9, records=3)) - - def test_NodeStatsDefaults(self): - stats_node.stats_node.value = True - rrd = Rrd('stats/node', stats_node.stats_node_step.value, stats_node.stats_node_rras.value) - - ts = int(time.time()) - for i in range(10): - rrd['user'].put({'total': i}, ts + i) - - volume = db.Volume('db', model.RESOURCES) - cp = NodeRoutes('guid', volume) - - self.assertEqual({ - 'user': [ - (ts + 0, {'total': 0.0}), - (ts + 1, {'total': 1.0}), - (ts + 2, {'total': 2.0}), - (ts + 3, {'total': 3.0}), - (ts + 4, {'total': 4.0}), - (ts + 5, {'total': 5.0}), - (ts + 6, {'total': 6.0}), - (ts + 7, {'total': 7.0}), - (ts + 8, {'total': 8.0}), - (ts + 9, {'total': 9.0}), - ], - }, - call(cp, method='GET', cmd='stats', source='user.total')) - def test_HandleDeletes(self): volume = db.Volume('db', model.RESOURCES) - volume['user'].create({'guid': tests.UID, 'name': 'user', 'pubkey': {'blob': StringIO(tests.PUBKEY)}}) - cp = NodeRoutes('guid', volume) + cp = NodeRoutes('guid', volume=volume) + volume['user'].create({'guid': tests.UID, 'name': 'user', 'pubkey': tests.PUBKEY}) guid = call(cp, method='POST', document='context', principal=tests.UID, content={ 'type': 'activity', @@ -192,33 +133,41 @@ class NodeTest(tests.Test): coroutine.dispatch() self.assertRaises(http.NotFound, call, cp, method='GET', document='context', guid=guid, reply=['guid', 'title']) self.assertEqual(['deleted'], volume['context'].get(guid)['layer']) - self.assertEqual({'event': 'delete', 'resource': 'context', 'guid': guid}, events[0]) - def test_SimulateDeleteEvents(self): - volume = db.Volume('db', model.RESOURCES) - volume['user'].create({'guid': tests.UID, 'name': 'user', 'pubkey': {'blob': StringIO(tests.PUBKEY)}}) - cp = NodeRoutes('guid', volume) + def test_DeletedRestoredHandlers(self): + trigger = [] - guid = call(cp, method='POST', document='context', principal=tests.UID, content={ - 'type': 'activity', - 'title': 'title', - 'summary': 'summary', - 'description': 'description', - }) + class TestDocument(db.Resource): - def subscribe(): - for event in cp.subscribe(): - events.append(event) - events = [] - coroutine.spawn(subscribe) - coroutine.dispatch() + def deleted(self): + trigger.append(False) - call(cp, method='PUT', document='context', guid=guid, principal=tests.UID, content={'layer': ['deleted']}) - coroutine.dispatch() - self.assertEqual({'event': 'delete', 'resource': 'context', 'guid': guid}, events[0]) + def restored(self): + trigger.append(True) + + volume = self.start_master([TestDocument, User]) + conn = Connection(auth=http.SugarAuth(keyfile.value)) + + guid = conn.post(['testdocument'], {}) + self.assertEqual([], trigger) + + conn.put(['testdocument', guid, 'layer'], ['deleted']) + self.assertEqual([False], trigger) + + conn.put(['testdocument', guid, 'layer'], []) + self.assertEqual([False, True], trigger) + + conn.put(['testdocument', guid, 'layer'], ['bar']) + self.assertEqual([False, True], trigger) + + conn.put(['testdocument', guid, 'layer'], ['deleted']) + self.assertEqual([False, True, False], trigger) + + conn.put(['testdocument', guid, 'layer'], ['deleted', 'foo']) + self.assertEqual([False, True, False], trigger) def test_RegisterUser(self): - cp = NodeRoutes('guid', db.Volume('db', [User])) + cp = NodeRoutes('guid', volume=db.Volume('db', [User])) guid = call(cp, method='POST', document='user', principal=tests.UID2, content={ 'name': 'user', @@ -229,7 +178,7 @@ class NodeTest(tests.Test): def test_UnauthorizedCommands(self): volume = db.Volume('db', model.RESOURCES) - volume['user'].create({'guid': tests.UID, 'name': 'user', 'pubkey': {'blob': StringIO(tests.PUBKEY)}}) + volume['user'].create({'guid': tests.UID, 'name': 'user', 'pubkey': tests.PUBKEY}) class Routes(NodeRoutes): @@ -244,7 +193,7 @@ class NodeTest(tests.Test): class Document(db.Resource): pass - cp = Routes('guid', db.Volume('db', [User, Document])) + cp = Routes('guid', volume=db.Volume('db', [User, Document])) guid = call(cp, method='POST', document='document', principal=tests.UID, content={}) self.assertRaises(http.Unauthorized, call, cp, method='GET', cmd='probe1', document='document', guid=guid) @@ -267,8 +216,8 @@ class NodeTest(tests.Test): pass volume = db.Volume('db', [User, Document]) - volume['user'].create({'guid': tests.UID, 'name': 'user', 'pubkey': {'blob': StringIO(tests.PUBKEY)}}) - cp = Routes('guid', volume) + cp = Routes('guid', volume=volume) + volume['user'].create({'guid': tests.UID, 'name': 'user', 'pubkey': tests.PUBKEY}) guid = call(cp, method='POST', document='document', principal=tests.UID, content={}) @@ -278,7 +227,7 @@ class NodeTest(tests.Test): call(cp, method='GET', cmd='probe2', document='document', guid=guid) def test_ForbiddenCommandsForUserResource(self): - cp = NodeRoutes('guid', db.Volume('db', [User])) + cp = NodeRoutes('guid', volume=db.Volume('db', [User])) call(cp, method='POST', document='user', principal=tests.UID2, content={ 'name': 'user1', @@ -304,9 +253,9 @@ class NodeTest(tests.Test): return 'ok' volume = db.Volume('db', [User]) - volume['user'].create({'guid': tests.UID, 'name': 'user', 'pubkey': {'blob': StringIO(tests.PUBKEY)}}) - volume['user'].create({'guid': tests.UID2, 'name': 'test', 'pubkey': {'blob': StringIO(tests.PUBKEY2)}}) - cp = Routes('guid', volume) + cp = Routes('guid', volume=volume) + volume['user'].create({'guid': tests.UID, 'name': 'user', 'pubkey': tests.PUBKEY}) + volume['user'].create({'guid': tests.UID2, 'name': 'test', 'pubkey': tests.PUBKEY2}) self.assertRaises(http.Forbidden, call, cp, method='PROBE') self.assertRaises(http.Forbidden, call, cp, method='PROBE', principal=tests.UID2) @@ -321,9 +270,9 @@ class NodeTest(tests.Test): return value volume = db.Volume('db', [User, Document]) - volume['user'].create({'guid': tests.UID, 'name': 'user', 'pubkey': {'blob': StringIO(tests.PUBKEY)}}) - volume['user'].create({'guid': tests.UID2, 'name': 'user', 'pubkey': {'blob': StringIO(tests.PUBKEY2)}}) - cp = NodeRoutes('guid', volume) + cp = NodeRoutes('guid', volume=volume) + volume['user'].create({'guid': tests.UID, 'name': 'user', 'pubkey': tests.PUBKEY}) + volume['user'].create({'guid': tests.UID2, 'name': 'user', 'pubkey': tests.PUBKEY2}) guid = call(cp, method='POST', document='document', principal=tests.UID, content={'prop': '1'}) self.assertRaises(http.Forbidden, call, cp, 'PUT', document='document', guid=guid, content={'prop': '2'}, principal=tests.UID2) @@ -342,9 +291,9 @@ class NodeTest(tests.Test): return value volume = db.Volume('db', [User, Document]) - volume['user'].create({'guid': tests.UID, 'name': 'user', 'pubkey': {'blob': StringIO(tests.PUBKEY)}}) - volume['user'].create({'guid': tests.UID2, 'name': 'user', 'pubkey': {'blob': StringIO(tests.PUBKEY2)}}) - cp = NodeRoutes('guid', volume) + cp = NodeRoutes('guid', volume=volume) + volume['user'].create({'guid': tests.UID, 'name': 'user', 'pubkey': tests.PUBKEY}) + volume['user'].create({'guid': tests.UID2, 'name': 'user', 'pubkey': tests.PUBKEY2}) guid = call(cp, method='POST', document='document', principal=tests.UID, content={'prop': '1'}) @@ -363,8 +312,8 @@ class NodeTest(tests.Test): pass volume = db.Volume('db', [User]) - volume['user'].create({'guid': tests.UID, 'name': 'user', 'pubkey': {'blob': StringIO(tests.PUBKEY)}}) - cp = Routes('guid', volume) + cp = Routes('guid', volume=volume) + volume['user'].create({'guid': tests.UID, 'name': 'user', 'pubkey': tests.PUBKEY}) self.assertRaises(http.Forbidden, call, cp, 'PROBE', principal=tests.UID) self.touch(('authorization.conf', [ @@ -386,7 +335,7 @@ class NodeTest(tests.Test): pass volume = db.Volume('db', [User]) - cp = Routes('guid', volume) + cp = Routes('guid', volume=volume) self.assertRaises(http.Unauthorized, call, cp, 'PROBE1') self.assertRaises(http.Forbidden, call, cp, 'PROBE2') @@ -401,8 +350,8 @@ class NodeTest(tests.Test): def test_SetUser(self): volume = db.Volume('db', model.RESOURCES) - volume['user'].create({'guid': tests.UID, 'name': 'user', 'pubkey': {'blob': StringIO(tests.PUBKEY)}}) - cp = NodeRoutes('guid', volume) + cp = NodeRoutes('guid', volume=volume) + volume['user'].create({'guid': tests.UID, 'name': 'user', 'pubkey': tests.PUBKEY}) guid = call(cp, method='POST', document='context', principal=tests.UID, content={ 'type': 'activity', @@ -416,8 +365,8 @@ class NodeTest(tests.Test): def test_find_MaxLimit(self): volume = db.Volume('db', model.RESOURCES) - volume['user'].create({'guid': tests.UID, 'name': 'user', 'pubkey': {'blob': StringIO(tests.PUBKEY)}}) - cp = NodeRoutes('guid', volume) + cp = NodeRoutes('guid', volume=volume) + volume['user'].create({'guid': tests.UID, 'name': 'user', 'pubkey': tests.PUBKEY}) call(cp, method='POST', document='context', principal=tests.UID, content={ 'type': 'activity', @@ -438,23 +387,26 @@ class NodeTest(tests.Test): 'description': 'description', }) - node.find_limit.value = 3 + cp._find_limit = 3 self.assertEqual(3, len(call(cp, method='GET', document='context', limit=1024)['result'])) - node.find_limit.value = 2 + cp._find_limit = 2 self.assertEqual(2, len(call(cp, method='GET', document='context', limit=1024)['result'])) - node.find_limit.value = 1 + cp._find_limit = 1 self.assertEqual(1, len(call(cp, method='GET', document='context', limit=1024)['result'])) def test_DeletedDocuments(self): volume = db.Volume('db', model.RESOURCES) - volume['user'].create({'guid': tests.UID, 'name': 'user', 'pubkey': {'blob': StringIO(tests.PUBKEY)}}) - cp = NodeRoutes('guid', volume) + cp = NodeRoutes('guid', volume=volume) + volume['user'].create({'guid': tests.UID, 'name': 'user', 'pubkey': tests.PUBKEY}) guid = call(cp, method='POST', document='context', principal=tests.UID, content={ 'type': 'activity', 'title': 'title1', 'summary': 'summary', 'description': 'description', + 'artifact_icon': '', + 'icon': '', + 'logo': '', }) call(cp, method='GET', document='context', guid=guid) @@ -468,8 +420,8 @@ class NodeTest(tests.Test): def test_CreateGUID(self): # TODO Temporal security hole, see TODO volume = db.Volume('db', model.RESOURCES) - volume['user'].create({'guid': tests.UID, 'name': 'user', 'pubkey': {'blob': StringIO(tests.PUBKEY)}}) - cp = NodeRoutes('guid', volume) + cp = NodeRoutes('guid', volume=volume) + volume['user'].create({'guid': tests.UID, 'name': 'user', 'pubkey': tests.PUBKEY}) call(cp, method='POST', document='context', principal=tests.UID, content={ 'guid': 'foo', 'type': 'activity', @@ -483,10 +435,10 @@ class NodeTest(tests.Test): def test_CreateMalformedGUID(self): volume = db.Volume('db', model.RESOURCES) - volume['user'].create({'guid': tests.UID, 'name': 'user', 'pubkey': {'blob': StringIO(tests.PUBKEY)}}) cp = MasterRoutes('guid', volume) + volume['user'].create({'guid': tests.UID, 'name': 'user', 'pubkey': tests.PUBKEY}) - self.assertRaises(RuntimeError, call, cp, method='POST', document='context', principal=tests.UID, content={ + self.assertRaises(http.BadRequest, call, cp, method='POST', document='context', principal=tests.UID, content={ 'guid': '!?', 'type': 'activity', 'title': 'title', @@ -496,8 +448,8 @@ class NodeTest(tests.Test): def test_FailOnExistedGUID(self): volume = db.Volume('db', model.RESOURCES) - volume['user'].create({'guid': tests.UID, 'name': 'user', 'pubkey': {'blob': StringIO(tests.PUBKEY)}}) cp = MasterRoutes('guid', volume) + volume['user'].create({'guid': tests.UID, 'name': 'user', 'pubkey': tests.PUBKEY}) guid = call(cp, method='POST', document='context', principal=tests.UID, content={ 'type': 'activity', @@ -506,7 +458,7 @@ class NodeTest(tests.Test): 'description': 'description', }) - self.assertRaises(RuntimeError, call, cp, method='POST', document='context', principal=tests.UID, content={ + self.assertRaises(http.BadRequest, call, cp, method='POST', document='context', principal=tests.UID, content={ 'guid': guid, 'type': 'activity', 'title': 'title', @@ -566,139 +518,9 @@ class NodeTest(tests.Test): def test_Clone(self): volume = self.start_master() - client = Connection(auth=http.SugarAuth(keyfile.value)) + client = http.Connection(api_url.value, http.SugarAuth(keyfile.value)) - context = client.post(['context'], { - 'type': 'activity', - 'title': 'title', - 'summary': 'summary', - 'description': 'description', - }) - impl1 = client.post(['release'], { - 'context': context, - 'license': 'GPLv3+', - 'version': '1', - 'stability': 'stable', - 'notes': '', - }) - blob1 = self.zips(('topdir/probe', 'probe1')) - volume['release'].update(impl1, {'data': { - 'blob': StringIO(blob1), - 'spec': { - '*-*': { - 'requires': { - 'dep1': {}, - }, - }, - }, - }}) - impl2 = client.post(['release'], { - 'context': context, - 'license': 'GPLv3+', - 'version': '2', - 'stability': 'stable', - 'notes': '', - }) - blob2 = self.zips(('topdir/probe', 'probe2')) - volume['release'].update(impl2, {'data': { - 'blob': StringIO(blob2), - 'spec': { - '*-*': { - 'requires': { - 'dep2': {'restrictions': [[None, '2']]}, - 'dep3': {}, - }, - }, - }, - }}) - impl3 = client.post(['release'], { - 'context': context, - 'license': 'GPLv3+', - 'version': '3', - 'stability': 'stable', - 'notes': '', - }) - blob3 = self.zips(('topdir/probe', 'probe3')) - volume['release'].update(impl3, {'data': { - 'blob': StringIO(blob3), - 'spec': { - '*-*': { - 'requires': { - 'dep2': {'restrictions': [['2', None]]}, - }, - }, - }, - }}) - impl4 = client.post(['release'], { - 'context': context, - 'license': 'GPLv3+', - 'version': '4', - 'stability': 'developer', - 'notes': '', - }) - blob4 = self.zips(('topdir/probe', 'probe4')) - volume['release'].update(impl4, {'data': { - 'blob': StringIO(blob4), - 'spec': { - '*-*': { - 'requires': {}, - }, - }, - }}) - - self.assertEqual(blob3, client.get(['context', context], cmd='clone')) - self.assertEqual(blob4, client.get(['context', context], cmd='clone', stability='developer')) - self.assertEqual(blob1, client.get(['context', context], cmd='clone', version='1')) - - self.assertEqual(blob1, client.get(['context', context], cmd='clone', requires='dep1')) - self.assertEqual(blob3, client.get(['context', context], cmd='clone', requires='dep2')) - self.assertEqual(blob2, client.get(['context', context], cmd='clone', requires='dep2=1')) - self.assertEqual(blob3, client.get(['context', context], cmd='clone', requires='dep2=2')) - self.assertEqual(blob2, client.get(['context', context], cmd='clone', requires='dep3')) - - self.assertRaises(http.NotFound, client.get, ['context', context], cmd='clone', requires='dep4') - self.assertRaises(http.NotFound, client.get, ['context', context], cmd='clone', stability='foo') - - response = Response() - client.call(Request(method='GET', path=['context', context], cmd='clone'), response) - self.assertEqual({ - 'context': context, - 'stability': 'stable', - 'guid': impl3, - 'version': '3', - 'license': ['GPLv3+'], - 'layer': ['origin'], - 'author': {tests.UID: {'name': tests.UID, 'order': 0, 'role': 3}}, - 'ctime': self.node_volume['release'].get(impl3).ctime, - 'notes': {'en-us': ''}, - 'tags': [], - 'data': { - 'blob_size': len(blob3), - 'spec': { - '*-*': { - 'requires': { - 'dep2': { - 'restrictions': [['2', None]], - }, - }, - }, - }, - }, - }, - response.meta) - - def test_release(self): - volume = self.start_master() - conn = Connection(auth=http.SugarAuth(keyfile.value)) - - conn.post(['context'], { - 'guid': 'bundle_id', - 'type': 'activity', - 'title': 'title', - 'summary': 'summary', - 'description': 'description', - }) - activity_info = '\n'.join([ + blob1 = self.zips(('topdir/activity/activity.info', '\n'.join([ '[Activity]', 'name = TestActivitry', 'bundle_id = bundle_id', @@ -706,42 +528,12 @@ class NodeTest(tests.Test): 'icon = icon', 'activity_version = 1', 'license = Public Domain', - 'stability = developer', - 'requires = sugar>=0.88; dep' - ]) - changelog = "LOG" - bundle1 = self.zips( - ('topdir/activity/activity.info', activity_info), - ('topdir/CHANGELOG', changelog), - ) - guid1 = json.load(conn.request('POST', ['release'], bundle1, params={'cmd': 'submit'}).raw) - - impl = volume['release'].get(guid1) - self.assertEqual('bundle_id', impl['context']) - self.assertEqual('1', impl['version']) - self.assertEqual('developer', impl['stability']) - self.assertEqual(['Public Domain'], impl['license']) - self.assertEqual('developer', impl['stability']) - self.assertEqual({'en-us': changelog}, impl['notes']) - assert impl['ctime'] > 0 - assert impl['mtime'] > 0 - self.assertEqual({tests.UID: {'role': 3, 'name': 'f470db873b6a35903aca1f2492188e1c4b9ffc42', 'order': 0}}, impl['author']) - - data = impl.meta('data') - self.assertEqual({ - '*-*': { - 'commands': {'activity': {'exec': 'true'}}, - 'requires': {'dep': {}, 'sugar': {'restrictions': [['0.88', None]]}}, - }, - }, - data['spec']) - - self.assertEqual('application/vnd.olpc-sugar', data['mime_type']) - self.assertEqual(len(bundle1), data['blob_size']) - self.assertEqual(len(activity_info) + len(changelog), data.get('unpack_size')) - self.assertEqual(bundle1, conn.get(['context', 'bundle_id'], cmd='clone', stability='developer')) + 'requires = dep1', + 'stability = stable', + ]))) + release1 = json.load(client.request('POST', ['context'], blob1, params={'cmd': 'submit', 'initial': True}).raw) - activity_info = '\n'.join([ + blob2 = self.zips(('topdir/activity/activity.info', '\n'.join([ '[Activity]', 'name = TestActivitry', 'bundle_id = bundle_id', @@ -749,606 +541,152 @@ class NodeTest(tests.Test): 'icon = icon', 'activity_version = 2', 'license = Public Domain', + 'requires = dep2 < 3; dep3', 'stability = stable', - ]) - bundle2 = self.zips(('topdir/activity/activity.info', activity_info)) - guid2 = json.load(conn.request('POST', ['release'], bundle2, params={'cmd': 'submit'}).raw) - - self.assertEqual('1', volume['release'].get(guid1)['version']) - self.assertEqual(['origin'], volume['release'].get(guid1)['layer']) - self.assertEqual('2', volume['release'].get(guid2)['version']) - self.assertEqual(['origin'], volume['release'].get(guid2)['layer']) - self.assertEqual(bundle2, conn.get(['context', 'bundle_id'], cmd='clone')) + ]))) + release2 = json.load(client.request('POST', ['context'], blob2, params={'cmd': 'submit'}).raw) - activity_info = '\n'.join([ + blob3 = self.zips(('topdir/activity/activity.info', '\n'.join([ '[Activity]', 'name = TestActivitry', 'bundle_id = bundle_id', 'exec = true', 'icon = icon', - 'activity_version = 1', + 'activity_version = 3', 'license = Public Domain', + 'requires = dep2 >= 2', 'stability = stable', - ]) - bundle3 = self.zips(('topdir/activity/activity.info', activity_info)) - guid3 = json.load(conn.request('POST', ['release'], bundle3, params={'cmd': 'submit'}).raw) - - self.assertEqual('1', volume['release'].get(guid1)['version']) - self.assertEqual(sorted(['origin', 'deleted']), sorted(volume['release'].get(guid1)['layer'])) - self.assertEqual('2', volume['release'].get(guid2)['version']) - self.assertEqual(['origin'], volume['release'].get(guid2)['layer']) - self.assertEqual('1', volume['release'].get(guid3)['version']) - self.assertEqual(['origin'], volume['release'].get(guid3)['layer']) - self.assertEqual(bundle2, conn.get(['context', 'bundle_id'], cmd='clone')) + ]))) + release3 = json.load(client.request('POST', ['context'], blob3, params={'cmd': 'submit'}).raw) - activity_info = '\n'.join([ + blob4 = self.zips(('topdir/activity/activity.info', '\n'.join([ '[Activity]', 'name = TestActivitry', 'bundle_id = bundle_id', 'exec = true', 'icon = icon', - 'activity_version = 2', + 'activity_version = 4', 'license = Public Domain', - 'stability = buggy', - ]) - bundle4 = self.zips(('topdir/activity/activity.info', activity_info)) - guid4 = json.load(conn.request('POST', ['release'], bundle4, params={'cmd': 'submit'}).raw) - - self.assertEqual('1', volume['release'].get(guid1)['version']) - self.assertEqual(sorted(['origin', 'deleted']), sorted(volume['release'].get(guid1)['layer'])) - self.assertEqual('2', volume['release'].get(guid2)['version']) - self.assertEqual(sorted(['origin', 'deleted']), sorted(volume['release'].get(guid2)['layer'])) - self.assertEqual('1', volume['release'].get(guid3)['version']) - self.assertEqual(['origin'], volume['release'].get(guid3)['layer']) - self.assertEqual('2', volume['release'].get(guid4)['version']) - self.assertEqual(['origin'], volume['release'].get(guid4)['layer']) - self.assertEqual(bundle3, conn.get(['context', 'bundle_id'], cmd='clone')) - - def test_release_UpdateContext(self): - volume = self.start_master() - conn = Connection(auth=http.SugarAuth(keyfile.value)) + 'stability = developer', + ]))) + release4 = json.load(client.request('POST', ['context'], blob4, params={'cmd': 'submit'}).raw) - conn.post(['context'], { - 'guid': 'org.laptop.ImageViewerActivity', - 'type': 'activity', - 'title': {'en': ''}, - 'summary': {'en': ''}, - 'description': {'en': ''}, - }) - svg = '\n'.join([ - '<?xml version="1.0" encoding="UTF-8"?>', - '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [', - ' <!ENTITY fill_color "#123456">', - ' <!ENTITY stroke_color "#123456">', - ']>', - '<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50">', - ' <rect x="3" y="7" width="44" height="36" style="fill:&fill_color;;stroke:&stroke_color;;stroke-width:3"/>', - ' <polyline points="15,7 25,1 35,7" style="fill:none;;stroke:&stroke_color;;stroke-width:1.25"/>', - ' <circle cx="14" cy="19" r="4.5" style="fill:&stroke_color;;stroke:&stroke_color;;stroke-width:1.5"/>', - ' <polyline points="3,36 16,32 26,35" style="fill:none;;stroke:&stroke_color;;stroke-width:2.5"/>', - ' <polyline points="15,43 37,28 47,34 47,43" style="fill:&stroke_color;;stroke:&stroke_color;;stroke-width:3"/>', - ' <polyline points="22,41.5 35,30 27,41.5" style="fill:&fill_color;;stroke:none;;stroke-width:0"/>', - ' <polyline points="26,23 28,25 30,23" style="fill:none;;stroke:&stroke_color;;stroke-width:.9"/>', - ' <polyline points="31.2,20 33.5,17.7 35.8,20" style="fill:none;;stroke:&stroke_color;;stroke-width:1"/>', - ' <polyline points="36,13 38.5,15.5 41,13" style="fill:none;;stroke:&stroke_color;;stroke-width:1"/>', - '</svg>', - ]) - bundle = self.zips( - ('ImageViewer.activity/activity/activity.info', '\n'.join([ - '[Activity]', - 'bundle_id = org.laptop.ImageViewerActivity', - 'name = Image Viewer', - 'summary = The Image Viewer activity is a simple and fast image viewer tool', - 'description = It has features one would expect of a standard image viewer, like zoom, rotate, etc.', - 'homepage = http://wiki.sugarlabs.org/go/Activities/Image_Viewer', - 'activity_version = 22', - 'license = GPLv2+', - 'icon = activity-imageviewer', - 'exec = true', - 'mime_types = image/bmp;image/gif', - ])), - ('ImageViewer.activity/locale/ru/LC_MESSAGES/org.laptop.ImageViewerActivity.mo', - base64.b64decode('3hIElQAAAAAMAAAAHAAAAHwAAAARAAAA3AAAAAAAAAAgAQAADwAAACEBAAAOAAAAMQEAAA0AAABAAQAACgAAAE4BAAAMAAAAWQEAAA0AAABmAQAAJwAAAHQBAAAUAAAAnAEAABAAAACxAQAABwAAAMIBAAAIAAAAygEAANEBAADTAQAAIQAAAKUDAAATAAAAxwMAABwAAADbAwAAFwAAAPgDAAAhAAAAEAQAAB0AAAAyBAAAQAAAAFAEAAA9AAAAkQQAADUAAADPBAAAFAAAAAUFAAAQAAAAGgUAAAEAAAACAAAABwAAAAAAAAADAAAAAAAAAAwAAAAJAAAAAAAAAAoAAAAEAAAAAAAAAAAAAAALAAAABgAAAAgAAAAFAAAAAENob29zZSBkb2N1bWVudABEb3dubG9hZGluZy4uLgBGaXQgdG8gd2luZG93AEZ1bGxzY3JlZW4ASW1hZ2UgVmlld2VyAE9yaWdpbmFsIHNpemUAUmV0cmlldmluZyBzaGFyZWQgaW1hZ2UsIHBsZWFzZSB3YWl0Li4uAFJvdGF0ZSBhbnRpY2xvY2t3aXNlAFJvdGF0ZSBjbG9ja3dpc2UAWm9vbSBpbgBab29tIG91dABQcm9qZWN0LUlkLVZlcnNpb246IFBBQ0tBR0UgVkVSU0lPTgpSZXBvcnQtTXNnaWQtQnVncy1UbzogClBPVC1DcmVhdGlvbi1EYXRlOiAyMDEyLTA5LTI3IDE0OjU3LTA0MDAKUE8tUmV2aXNpb24tRGF0ZTogMjAxMC0wOS0yMiAxMzo1MCswMjAwCkxhc3QtVHJhbnNsYXRvcjoga3JvbTlyYSA8a3JvbTlyYUBnbWFpbC5jb20+Ckxhbmd1YWdlLVRlYW06IExBTkdVQUdFIDxMTEBsaS5vcmc+Ckxhbmd1YWdlOiAKTUlNRS1WZXJzaW9uOiAxLjAKQ29udGVudC1UeXBlOiB0ZXh0L3BsYWluOyBjaGFyc2V0PVVURi04CkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6IDhiaXQKUGx1cmFsLUZvcm1zOiBucGx1cmFscz0zOyBwbHVyYWw9KG4lMTA9PTEgJiYgbiUxMDAhPTExID8gMCA6IG4lMTA+PTIgJiYgbiUxMDw9NCAmJiAobiUxMDA8MTAgfHwgbiUxMDA+PTIwKSA/IDEgOiAyKTsKWC1HZW5lcmF0b3I6IFBvb3RsZSAyLjAuMwoA0JLRi9Cx0LXRgNC40YLQtSDQtNC+0LrRg9C80LXQvdGCANCX0LDQs9GA0YPQt9C60LAuLi4A0KPQvNC10YHRgtC40YLRjCDQsiDQvtC60L3QtQDQn9C+0LvQvdGL0Lkg0Y3QutGA0LDQvQDQn9GA0L7RgdC80L7RgtGAINC60LDRgNGC0LjQvdC+0LoA0JjRgdGC0LjQvdC90YvQuSDRgNCw0LfQvNC10YAA0J/QvtC70YPRh9C10L3QuNC1INC40LfQvtCx0YDQsNC20LXQvdC40LksINC/0L7QtNC+0LbQtNC40YLQtS4uLgDQn9C+0LLQtdGA0L3Rg9GC0Ywg0L/RgNC+0YLQuNCyINGH0LDRgdC+0LLQvtC5INGB0YLRgNC10LvQutC4ANCf0L7QstC10YDQvdGD0YLRjCDQv9C+INGH0LDRgdC+0LLQvtC5INGB0YLRgNC10LvQutC1ANCf0YDQuNCx0LvQuNC30LjRgtGMANCe0YLQtNCw0LvQuNGC0YwA')), - ('ImageViewer.activity/activity/activity-imageviewer.svg', svg), - ) - impl = json.load(conn.request('POST', ['release'], bundle, params={'cmd': 'submit'}).raw) + assert blob3 == client.get(['context', 'bundle_id'], cmd='clone') + assert blob4 == client.get(['context', 'bundle_id'], cmd='clone', stability='developer') + assert blob1 == client.get(['context', 'bundle_id'], cmd='clone', version='1') - context = volume['context'].get('org.laptop.ImageViewerActivity') - self.assertEqual({ - 'en': 'Image Viewer', - 'ru': u'Просмотр картинок', - }, - context['title']) - self.assertEqual({ - 'en': 'The Image Viewer activity is a simple and fast image viewer tool', - }, - context['summary']) - self.assertEqual({ - 'en': 'It has features one would expect of a standard image viewer, like zoom, rotate, etc.', - }, - context['description']) - self.assertEqual(svg, file(context['artifact_icon']['blob']).read()) - assert 'blob' in context['icon'] - assert 'blob' in context['logo'] - self.assertEqual('http://wiki.sugarlabs.org/go/Activities/Image_Viewer', context['homepage']) - self.assertEqual(['image/bmp', 'image/gif'], context['mime_types']) - - def test_release_CreateContext(self): - volume = self.start_master() - conn = Connection(auth=http.SugarAuth(keyfile.value)) - - bundle = self.zips( - ('ImageViewer.activity/activity/activity.info', '\n'.join([ - '[Activity]', - 'bundle_id = org.laptop.ImageViewerActivity', - 'name = Image Viewer', - 'summary = The Image Viewer activity is a simple and fast image viewer tool', - 'description = It has features one would expect of a standard image viewer, like zoom, rotate, etc.', - 'homepage = http://wiki.sugarlabs.org/go/Activities/Image_Viewer', - 'activity_version = 22', - 'license = GPLv2+', - 'icon = activity-imageviewer', - 'exec = true', - 'mime_types = image/bmp;image/gif', - ])), - ('ImageViewer.activity/activity/activity-imageviewer.svg', ''), - ) - self.assertRaises(http.NotFound, conn.request, 'POST', ['release'], bundle, params={'cmd': 'submit'}) - impl = json.load(conn.request('POST', ['release'], bundle, params={'cmd': 'submit', 'initial': 1}).raw) - - context = volume['context'].get('org.laptop.ImageViewerActivity') - self.assertEqual({'en': 'Image Viewer'}, context['title']) - self.assertEqual({'en': 'The Image Viewer activity is a simple and fast image viewer tool'}, context['summary']) - self.assertEqual({'en': 'It has features one would expect of a standard image viewer, like zoom, rotate, etc.'}, context['description']) - self.assertEqual('http://wiki.sugarlabs.org/go/Activities/Image_Viewer', context['homepage']) - self.assertEqual(['image/bmp', 'image/gif'], context['mime_types']) - assert context['ctime'] > 0 - assert context['mtime'] > 0 - self.assertEqual({tests.UID: {'role': 3, 'name': 'f470db873b6a35903aca1f2492188e1c4b9ffc42', 'order': 0}}, context['author']) - - def test_release_ByNonAuthors(self): - volume = self.start_master() - bundle = self.zips( - ('ImageViewer.activity/activity/activity.info', '\n'.join([ - '[Activity]', - 'bundle_id = org.laptop.ImageViewerActivity', - 'name = Image Viewer', - 'activity_version = 1', - 'license = GPLv2+', - 'icon = activity-imageviewer', - 'exec = true', - ])), - ('ImageViewer.activity/activity/activity-imageviewer.svg', ''), - ) + assert blob1 == client.get(['context', 'bundle_id'], cmd='clone', requires='dep1') + assert blob3 == client.get(['context', 'bundle_id'], cmd='clone', requires='dep2') + assert blob2 == client.get(['context', 'bundle_id'], cmd='clone', requires='dep2=1') + assert blob3 == client.get(['context', 'bundle_id'], cmd='clone', requires='dep2=2') + assert blob2 == client.get(['context', 'bundle_id'], cmd='clone', requires='dep3') - conn = Connection(auth=http.SugarAuth(join(tests.root, 'data', tests.UID))) - impl1 = json.load(conn.request('POST', ['release'], bundle, params={'cmd': 'submit', 'initial': 1}).raw) - impl2 = json.load(conn.request('POST', ['release'], bundle, params={'cmd': 'submit'}).raw) - self.assertEqual(sorted(['origin', 'deleted']), sorted(volume['release'].get(impl1)['layer'])) - self.assertEqual(['origin'], volume['release'].get(impl2)['layer']) + self.assertRaises(http.NotFound, client.get, ['context', 'bundle_id'], cmd='clone', requires='dep4') + self.assertRaises(http.NotFound, client.get, ['context', 'bundle_id'], cmd='clone', stability='foo') - conn = Connection(auth=http.SugarAuth(join(tests.root, 'data', tests.UID2))) - conn.get(cmd='whoami') - impl3 = json.load(conn.request('POST', ['release'], bundle, params={'cmd': 'submit'}).raw) - self.assertEqual(sorted(['origin', 'deleted']), sorted(volume['release'].get(impl1)['layer'])) - self.assertEqual(sorted(['origin', 'deleted']), sorted(volume['release'].get(impl2)['layer'])) - self.assertEqual([], volume['release'].get(impl3)['layer']) + response = Response() + client.call(Request(method='GET', path=['context', 'bundle_id'], cmd='clone'), response) + announce = next(volume['post'].find(query='3', limit=1)[0]).guid + self.assertEqual({ + 'license': ['Public Domain'], + 'unpack_size': 162, + 'stability': 'stable', + 'version': '3', + 'release': [[3], 0], + 'announce': announce, + 'requires': ['dep2-2'], + 'spec': { + '*-*': { + 'commands': {'activity': {'exec': u'true'}}, + 'requires': {'dep2': {'restrictions': [['2', None]]}}, + 'bundle': str(hash(blob3)), + }, + }, + }, response.meta) - def test_release_PopulateRequires(self): + def test_release(self): volume = self.start_master() conn = Connection(auth=http.SugarAuth(keyfile.value)) + activity_info = '\n'.join([ + '[Activity]', + 'name = TestActivitry', + 'bundle_id = bundle_id', + 'exec = true', + 'icon = icon', + 'activity_version = 1', + 'license = Public Domain', + 'stability = developer', + ]) + changelog = "LOG" bundle = self.zips( - ('ImageViewer.activity/activity/activity.info', '\n'.join([ - '[Activity]', - 'bundle_id = org.laptop.ImageViewerActivity', - 'name = Image Viewer', - 'activity_version = 22', - 'license = GPLv2+', - 'icon = activity-imageviewer', - 'exec = true', - 'requires = dep1, dep2<10, dep3<=20, dep4>30, dep5>=40, dep6>5<7, dep7>=1<=3', - ])), - ('ImageViewer.activity/activity/activity-imageviewer.svg', ''), + ('topdir/activity/activity.info', activity_info), + ('topdir/CHANGELOG', changelog), ) - self.assertRaises(http.NotFound, conn.request, 'POST', ['release'], bundle, params={'cmd': 'submit'}) - impl = json.load(conn.request('POST', ['release'], bundle, params={'cmd': 'submit', 'initial': 1}).raw) - - self.assertEqual( - sorted([ - 'dep1', 'dep2', 'dep3', 'dep4-31', 'dep5-40', - 'dep6-6', - 'dep7-1', 'dep7-2', 'dep7-3', - ]), - sorted(volume['release'].get(impl)['requires'])) - - def test_generate_node_stats_Posts(self): - node.stats_root.value = 'stats' - stats_node.stats_node.value = True - stats_node.stats_node_rras.value = ['RRA:AVERAGE:0.5:1:10', 'RRA:AVERAGE:0.5:10:10'] - volume = db.Volume('db', model.RESOURCES) - - ts = 1000000000 - - volume['user'].create({ - 'guid': 'user_1', - 'ctime': ts + 1, - 'mtime': ts + 1, - 'layer': [], - 'name': '', - }) - volume['context'].create({ - 'guid': 'context_1', - 'ctime': ts + 1, - 'mtime': ts + 1, - 'layer': [], - 'type': 'activity', - 'title': '', - 'summary': '', - 'description': '', - }) - volume['release'].create({ - 'guid': 'impl_1', - 'ctime': ts + 2, - 'mtime': ts + 2, - 'layer': [], - 'context': 'context_1', - 'license': ['GPL-3'], - 'version': '1', - }) - volume['post'].create({ - 'guid': 'topic_1', - 'ctime': ts + 3, - 'mtime': ts + 3, - 'layer': [], - 'context': 'context_1', - 'type': 'object', - 'title': '', - 'message': '', - }) - volume['post'].create({ - 'guid': 'solution_1', - 'ctime': ts + 5, - 'mtime': ts + 5, - 'layer': [], - 'context': 'context_1', - 'topic': 'topic_1', - 'title': '', - 'message': '', - 'type': 'answer', - }) - volume['post'].create({ - 'guid': 'review_1', - 'ctime': ts + 6, - 'mtime': ts + 6, - 'layer': [], - 'context': 'context_1', - 'vote': 1, - 'title': '', - 'message': '', - 'type': 'review', - }) - volume['post'].create({ - 'guid': 'review_2', - 'ctime': ts + 6, - 'mtime': ts + 6, - 'layer': [], - 'context': 'context_1', - 'topic': 'topic_1', - 'vote': 2, - 'title': '', - 'message': '', - 'type': 'feedback', - }) - volume['report'].create({ - 'guid': 'report_1', - 'ctime': ts + 8, - 'mtime': ts + 8, - 'layer': [], - 'context': 'context_1', - 'release': 'impl_1', - 'error': '', - }) - volume['user'].create({ - 'guid': 'user_2', - 'ctime': ts + 4, - 'mtime': ts + 4, - 'layer': [], - 'name': '', - }) - volume['context'].create({ - 'guid': 'context_2', - 'ctime': ts + 4, - 'mtime': ts + 4, - 'layer': [], - 'type': 'activity', - 'title': '', - 'summary': '', - 'description': '', - }) - volume['release'].create({ - 'guid': 'impl_2', - 'ctime': ts + 4, - 'mtime': ts + 4, - 'layer': [], - 'context': 'context_2', - 'license': ['GPL-3'], - 'version': '1', - }) - volume['release'].create({ - 'guid': 'impl_3', - 'ctime': ts + 4, - 'mtime': ts + 4, - 'layer': [], - 'context': 'context_2', - 'license': ['GPL-3'], - 'version': '1', - }) - volume['post'].create({ - 'guid': 'review_3', - 'ctime': ts + 4, - 'mtime': ts + 4, - 'layer': [], - 'context': 'context_2', - 'vote': 3, - 'title': '', - 'message': '', - 'type': 'review', - }) - volume['post'].create({ - 'guid': 'review_4', - 'ctime': ts + 4, - 'mtime': ts + 4, - 'layer': [], - 'context': 'context_2', - 'vote': 4, - 'title': '', - 'message': '', - 'type': 'review', - }) - volume['report'].create({ - 'guid': 'report_2', - 'ctime': ts + 4, - 'mtime': ts + 4, - 'layer': [], - 'context': 'context_2', - 'release': 'impl_1', - 'error': '', - }) - volume['report'].create({ - 'guid': 'report_3', - 'ctime': ts + 4, - 'mtime': ts + 4, - 'layer': [], - 'context': 'context_2', - 'release': 'impl_1', - 'error': '', - }) - volume['post'].create({ - 'guid': 'topic_2', - 'ctime': ts + 4, - 'mtime': ts + 4, - 'layer': [], - 'context': 'context_2', - 'type': 'object', - 'title': '', - 'message': '', - }) - volume['post'].create({ - 'guid': 'solution_2', - 'ctime': ts + 4, - 'mtime': ts + 4, - 'layer': [], - 'context': 'context_2', - 'topic': 'topic_2', - 'title': '', - 'message': '', - 'type': 'answer', - }) - - self.override(time, 'time', lambda: ts + 9) - old_stats = stats_node.Sniffer(volume, 'stats/node') - old_stats.log(Request(method='GET', path=['release', 'impl_1', 'data'])) - old_stats.log(Request(method='GET', path=['post', 'topic_1', 'data'])) - old_stats.commit(ts + 1) - old_stats.commit_objects() - old_stats.commit(ts + 2) - old_stats.commit(ts + 3) - old_stats.log(Request(method='GET', path=['release', 'impl_1', 'data'])) - old_stats.log(Request(method='GET', path=['release', 'impl_2', 'data'])) - old_stats.commit(ts + 4) - old_stats.commit_objects() - old_stats.commit(ts + 5) - old_stats.commit(ts + 6) - old_stats.log(Request(method='GET', path=['post', 'topic_1', 'data'])) - old_stats.log(Request(method='GET', path=['post', 'topic_2', 'data'])) - old_stats.commit(ts + 7) - old_stats.commit_objects() - old_stats.commit(ts + 8) - old_stats.commit_objects() - - generate_node_stats(volume, 'stats/node') - cp = NodeRoutes('guid', volume) + release = json.load(conn.request('POST', ['context'], bundle, params={'cmd': 'submit', 'initial': True}).raw) + announce = next(volume['post'].find(query='1', limit=1)[0]).guid self.assertEqual({ - 'user': [ - (ts + 1, {'total': 1.0}), - (ts + 2, {'total': 1.0}), - (ts + 3, {'total': 1.0}), - (ts + 4, {'total': 2.0}), - (ts + 5, {'total': 2.0}), - (ts + 6, {'total': 2.0}), - (ts + 7, {'total': 2.0}), - (ts + 8, {'total': 2.0}), - (ts + 9, {'total': 2.0}), - ], - 'context': [ - (ts + 1, {'total': 1.0, 'released': 0.0, 'failed': 0.0, 'downloaded': 1.0}), - (ts + 2, {'total': 1.0, 'released': 1.0, 'failed': 0.0, 'downloaded': 1.0}), - (ts + 3, {'total': 1.0, 'released': 1.0, 'failed': 0.0, 'downloaded': 1.0}), - (ts + 4, {'total': 2.0, 'released': 3.0, 'failed': 2.0, 'downloaded': 3.0}), - (ts + 5, {'total': 2.0, 'released': 3.0, 'failed': 2.0, 'downloaded': 3.0}), - (ts + 6, {'total': 2.0, 'released': 3.0, 'failed': 2.0, 'downloaded': 3.0}), - (ts + 7, {'total': 2.0, 'released': 3.0, 'failed': 2.0, 'downloaded': 3.0}), - (ts + 8, {'total': 2.0, 'released': 3.0, 'failed': 3.0, 'downloaded': 3.0}), - (ts + 9, {'total': 2.0, 'released': 3.0, 'failed': 3.0, 'downloaded': 3.0}), - ], - 'post': [ - (ts + 1, {'total': 0.0, 'downloaded': 1.0}), - (ts + 2, {'total': 0.0, 'downloaded': 1.0}), - (ts + 3, {'total': 1.0, 'downloaded': 1.0}), - (ts + 4, {'total': 5.0, 'downloaded': 1.0}), - (ts + 5, {'total': 6.0, 'downloaded': 1.0}), - (ts + 6, {'total': 8.0, 'downloaded': 1.0}), - (ts + 7, {'total': 8.0, 'downloaded': 3.0}), - (ts + 8, {'total': 8.0, 'downloaded': 3.0}), - (ts + 9, {'total': 8.0, 'downloaded': 3.0}), - ], - }, - call(cp, method='GET', cmd='stats', source=[ - 'user.total', - 'context.total', - 'context.released', - 'context.failed', - 'context.downloaded', - 'post.total', - 'post.downloaded', - ], start=ts + 1, end=ts + 10)) + release: { + 'seqno': 4, + 'author': {tests.UID: {'name': tests.UID, 'order': 0, 'role': 3}}, + 'value': { + 'license': ['Public Domain'], + 'announce': announce, + 'release': [[1], 0], + 'requires': [], + 'spec': {'*-*': {'bundle': str(hash(bundle)), 'commands': {'activity': {'exec': 'true'}}, 'requires': {}}}, + 'stability': 'developer', + 'unpack_size': len(activity_info) + len(changelog), + 'version': '1', + }, + }, + }, conn.get(['context', 'bundle_id', 'releases'])) + post = volume['post'][announce] + assert tests.UID in post['author'] + self.assertEqual('notification', post['type']) self.assertEqual({ - 'downloads': 2, - 'rating': [1, 1], - }, - volume['context'].get('context_1').properties(['downloads', 'rating'])) - self.assertEqual({ - 'downloads': 1, - 'rating': [2, 7], - }, - volume['context'].get('context_2').properties(['downloads', 'rating'])) - self.assertEqual({ - 'downloads': 2, - 'rating': [1, 2], - }, - volume['post'].get('topic_1').properties(['downloads', 'rating'])) - self.assertEqual({ - 'downloads': 1, - 'rating': [0, 0], - }, - volume['post'].get('topic_2').properties(['downloads', 'rating'])) - - def test_generate_node_stats_Deletes(self): - node.stats_root.value = 'stats' - stats_node.stats_node.value = True - stats_node.stats_node_rras.value = ['RRA:AVERAGE:0.5:1:10', 'RRA:AVERAGE:0.5:10:10'] - volume = db.Volume('db', model.RESOURCES) - - ts = 1000000000 - - volume['user'].create({ - 'guid': 'user_1', - 'ctime': ts + 1, - 'mtime': ts + 2, - 'layer': ['deleted'], - 'name': '', - }) - volume['context'].create({ - 'guid': 'context_1', - 'ctime': ts + 1, - 'mtime': ts + 2, - 'layer': ['deleted'], - 'type': 'activity', - 'title': '', - 'summary': '', - 'description': '', - }) - volume['release'].create({ - 'guid': 'impl_1', - 'ctime': ts + 1, - 'mtime': ts + 2, - 'layer': ['deleted'], - 'context': 'context_1', - 'license': ['GPL-3'], - 'version': '1', - }) - volume['post'].create({ - 'guid': 'post_1', - 'ctime': ts + 1, - 'mtime': ts + 2, - 'layer': ['deleted'], - 'context': 'context_1', - 'type': 'object', - 'title': '', - 'message': '', - }) - volume['report'].create({ - 'guid': 'report_1', - 'ctime': ts + 1, - 'mtime': ts + 2, - 'layer': ['deleted'], - 'context': 'context_1', - 'release': 'impl_1', - 'error': '', - }) - - self.override(time, 'time', lambda: ts + 9) - generate_node_stats(volume, 'stats/node') - cp = NodeRoutes('guid', volume) - + 'en': 'TestActivitry 1 release', + 'es': 'TestActivitry 1 release', + 'fr': 'TestActivitry 1 release', + }, post['title']) self.assertEqual({ - 'user': [ - (ts + 1, {'total': 1.0}), - (ts + 2, {'total': 0.0}), - (ts + 3, {'total': 0.0}), - ], - 'context': [ - (ts + 1, {'total': 1.0}), - (ts + 2, {'total': 0.0}), - (ts + 3, {'total': 0.0}), - ], - 'post': [ - (ts + 1, {'total': 1.0}), - (ts + 2, {'total': 0.0}), - (ts + 3, {'total': 0.0}), - ], - }, - call(cp, method='GET', cmd='stats', source=[ - 'user.total', - 'context.total', - 'post.total', - ], start=ts + 1, end=ts + 3)) + 'en-us': 'LOG', + }, post['message']) def test_AggpropInsertAccess(self): class Document(db.Resource): - @db.stored_property(typecast=db.AggregatedType, default=db.AggregatedType(), acl=ACL.READ | ACL.INSERT) + @db.stored_property(db.Aggregated, acl=ACL.READ | ACL.INSERT) def prop1(self, value): return value - @db.stored_property(typecast=db.AggregatedType, default=db.AggregatedType(), acl=ACL.READ | ACL.INSERT | ACL.AUTHOR) + @db.stored_property(db.Aggregated, acl=ACL.READ | ACL.INSERT | ACL.AUTHOR) def prop2(self, value): return value volume = db.Volume('db', [Document, User]) - volume['user'].create({'guid': tests.UID, 'name': 'user1', 'pubkey': {'blob': StringIO(tests.PUBKEY)}}) - volume['user'].create({'guid': tests.UID2, 'name': 'user2', 'pubkey': {'blob': StringIO(tests.PUBKEY2)}}) + cp = NodeRoutes('node', volume=volume) + volume['user'].create({'guid': tests.UID, 'name': 'user1', 'pubkey': tests.PUBKEY}) + volume['user'].create({'guid': tests.UID2, 'name': 'user2', 'pubkey': tests.PUBKEY2}) - cp = NodeRoutes('node', volume) guid = call(cp, method='POST', document='document', principal=tests.UID, content={}) self.override(time, 'time', lambda: 0) - call(cp, method='POST', path=['document', guid, 'prop1'], principal=tests.UID, content={'guid': '1'}) - call(cp, method='POST', path=['document', guid, 'prop1'], principal=tests.UID2, content={'guid': '2'}) + agg1 = call(cp, method='POST', path=['document', guid, 'prop1'], principal=tests.UID) + agg2 = call(cp, method='POST', path=['document', guid, 'prop1'], principal=tests.UID2) self.assertEqual({ - '1': {'seqno': 4, 'author': {tests.UID: {'name': 'user1', 'order': 0, 'role': 3}}}, - '2': {'seqno': 5, 'author': {tests.UID2: {'name': 'user2', 'order': 0, 'role': 3}}}, + agg1: {'seqno': 4, 'author': {tests.UID: {'name': 'user1', 'order': 0, 'role': 3}}, 'value': None}, + agg2: {'seqno': 5, 'author': {tests.UID2: {'name': 'user2', 'order': 0, 'role': 1}}, 'value': None}, }, call(cp, method='GET', path=['document', guid, 'prop1'])) - call(cp, method='POST', path=['document', guid, 'prop2'], principal=tests.UID, content={'guid': '1'}) - self.assertRaises(http. Forbidden, call, cp, method='POST', path=['document', guid, 'prop2'], principal=tests.UID2, content={'guid': '2'}) + agg3 = call(cp, method='POST', path=['document', guid, 'prop2'], principal=tests.UID) + self.assertRaises(http. Forbidden, call, cp, method='POST', path=['document', guid, 'prop2'], principal=tests.UID2) self.assertEqual({ - '1': {'seqno': 6, 'author': {tests.UID: {'name': 'user1', 'order': 0, 'role': 3}}}, + agg3: {'seqno': 6, 'author': {tests.UID: {'name': 'user1', 'order': 0, 'role': 3}}, 'value': None}, }, call(cp, method='GET', path=['document', guid, 'prop2'])) @@ -1356,64 +694,64 @@ class NodeTest(tests.Test): class Document(db.Resource): - @db.stored_property(typecast=db.AggregatedType, default=db.AggregatedType(), acl=ACL.READ | ACL.INSERT | ACL.REMOVE) + @db.stored_property(db.Aggregated, acl=ACL.READ | ACL.INSERT | ACL.REMOVE) def prop1(self, value): return value - @db.stored_property(typecast=db.AggregatedType, default=db.AggregatedType(), acl=ACL.READ | ACL.INSERT | ACL.REMOVE | ACL.AUTHOR) + @db.stored_property(db.Aggregated, acl=ACL.READ | ACL.INSERT | ACL.REMOVE | ACL.AUTHOR) def prop2(self, value): return value volume = db.Volume('db', [Document, User]) - volume['user'].create({'guid': tests.UID, 'name': 'user1', 'pubkey': {'blob': StringIO(tests.PUBKEY)}}) - volume['user'].create({'guid': tests.UID2, 'name': 'user2', 'pubkey': {'blob': StringIO(tests.PUBKEY2)}}) + cp = NodeRoutes('node', volume=volume) + volume['user'].create({'guid': tests.UID, 'name': 'user1', 'pubkey': tests.PUBKEY}) + volume['user'].create({'guid': tests.UID2, 'name': 'user2', 'pubkey': tests.PUBKEY2}) - cp = NodeRoutes('node', volume) guid = call(cp, method='POST', document='document', principal=tests.UID, content={}) self.override(time, 'time', lambda: 0) - call(cp, method='POST', path=['document', guid, 'prop1'], principal=tests.UID, content={'guid': '1', 'probe': True}) - call(cp, method='POST', path=['document', guid, 'prop1'], principal=tests.UID2, content={'guid': '2', 'probe': True}) + agg1 = call(cp, method='POST', path=['document', guid, 'prop1'], principal=tests.UID, content=True) + agg2 = call(cp, method='POST', path=['document', guid, 'prop1'], principal=tests.UID2, content=True) self.assertEqual({ - '1': {'seqno': 4, 'probe': True, 'author': {tests.UID: {'name': 'user1', 'order': 0, 'role': 3}}}, - '2': {'seqno': 5, 'probe': True, 'author': {tests.UID2: {'name': 'user2', 'order': 0, 'role': 3}}}, + agg1: {'seqno': 4, 'value': True, 'author': {tests.UID: {'name': 'user1', 'order': 0, 'role': 3}}}, + agg2: {'seqno': 5, 'value': True, 'author': {tests.UID2: {'name': 'user2', 'order': 0, 'role': 1}}}, }, call(cp, method='GET', path=['document', guid, 'prop1'])) - self.assertRaises(http.Forbidden, call, cp, method='DELETE', path=['document', guid, 'prop1', '1'], principal=tests.UID2) - self.assertRaises(http.Forbidden, call, cp, method='DELETE', path=['document', guid, 'prop1', '2'], principal=tests.UID) + self.assertRaises(http.Forbidden, call, cp, method='DELETE', path=['document', guid, 'prop1', agg1], principal=tests.UID2) + self.assertRaises(http.Forbidden, call, cp, method='DELETE', path=['document', guid, 'prop1', agg2], principal=tests.UID) self.assertEqual({ - '1': {'seqno': 4, 'probe': True, 'author': {tests.UID: {'name': 'user1', 'order': 0, 'role': 3}}}, - '2': {'seqno': 5, 'probe': True, 'author': {tests.UID2: {'name': 'user2', 'order': 0, 'role': 3}}}, + agg1: {'seqno': 4, 'value': True, 'author': {tests.UID: {'name': 'user1', 'order': 0, 'role': 3}}}, + agg2: {'seqno': 5, 'value': True, 'author': {tests.UID2: {'name': 'user2', 'order': 0, 'role': 1}}}, }, call(cp, method='GET', path=['document', guid, 'prop1'])) - call(cp, method='DELETE', path=['document', guid, 'prop1', '1'], principal=tests.UID) + call(cp, method='DELETE', path=['document', guid, 'prop1', agg1], principal=tests.UID) self.assertEqual({ - '1': {'seqno': 6}, - '2': {'seqno': 5, 'probe': True, 'author': {tests.UID2: {'name': 'user2', 'order': 0, 'role': 3}}}, + agg1: {'seqno': 6, 'author': {tests.UID: {'name': 'user1', 'order': 0, 'role': 3}}}, + agg2: {'seqno': 5, 'value': True, 'author': {tests.UID2: {'name': 'user2', 'order': 0, 'role': 1}}}, }, call(cp, method='GET', path=['document', guid, 'prop1'])) - call(cp, method='DELETE', path=['document', guid, 'prop1', '2'], principal=tests.UID2) + call(cp, method='DELETE', path=['document', guid, 'prop1', agg2], principal=tests.UID2) self.assertEqual({ - '1': {'seqno': 6}, - '2': {'seqno': 7}, + agg1: {'seqno': 6, 'author': {tests.UID: {'name': 'user1', 'order': 0, 'role': 3}}}, + agg2: {'seqno': 7, 'author': {tests.UID2: {'name': 'user2', 'order': 0, 'role': 1}}}, }, call(cp, method='GET', path=['document', guid, 'prop1'])) - call(cp, method='POST', path=['document', guid, 'prop2'], principal=tests.UID, content={'guid': '1', 'probe': True}) + agg3 = call(cp, method='POST', path=['document', guid, 'prop2'], principal=tests.UID, content=True) self.assertEqual({ - '1': {'seqno': 8, 'probe': True, 'author': {tests.UID: {'name': 'user1', 'order': 0, 'role': 3}}}, + agg3: {'seqno': 8, 'value': True, 'author': {tests.UID: {'name': 'user1', 'order': 0, 'role': 3}}}, }, call(cp, method='GET', path=['document', guid, 'prop2'])) - self.assertRaises(http.Forbidden, call, cp, method='DELETE', path=['document', guid, 'prop2', '1'], principal=tests.UID2) + self.assertRaises(http.Forbidden, call, cp, method='DELETE', path=['document', guid, 'prop2', agg3], principal=tests.UID2) self.assertEqual({ - '1': {'seqno': 8, 'probe': True, 'author': {tests.UID: {'name': 'user1', 'order': 0, 'role': 3}}}, + agg3: {'seqno': 8, 'value': True, 'author': {tests.UID: {'name': 'user1', 'order': 0, 'role': 3}}}, }, call(cp, method='GET', path=['document', guid, 'prop2'])) - call(cp, method='DELETE', path=['document', guid, 'prop2', '1'], principal=tests.UID) + call(cp, method='DELETE', path=['document', guid, 'prop2', agg3], principal=tests.UID) self.assertEqual({ - '1': {'seqno': 9}, + agg3: {'seqno': 9, 'author': {tests.UID: {'name': 'user1', 'order': 0, 'role': 3}}}, }, call(cp, method='GET', path=['document', guid, 'prop2'])) diff --git a/tests/units/node/obs.py b/tests/units/node/obs.py index bf43ed6..21b53a0 100755 --- a/tests/units/node/obs.py +++ b/tests/units/node/obs.py @@ -39,8 +39,8 @@ class ObsTest(tests.Test): ])) self.assertEqual([ - {'distributor_id': 'Debian', 'name': 'Debian-6.0', 'arches': ['i586', 'x86_64']}, - {'distributor_id': 'Fedora', 'name': 'Fedora-11', 'arches': ['i586']}, + {'lsb_id': 'Debian', 'lsb_release': '6.0', 'name': 'Debian-6.0', 'arches': ['i586', 'x86_64']}, + {'lsb_id': 'Fedora', 'lsb_release': '11', 'name': 'Fedora-11', 'arches': ['i586']}, ], obs.get_repos()) @@ -51,21 +51,10 @@ class ObsTest(tests.Test): 'project': 'base', 'repository': 'repo', 'arch': 'arch', - 'package': 'pkg1', + 'package': ['pkg1', 'pkg2'], }}, [ '<resolve>', ' <binary name="pygame" url="http://pkg1.prm" arch="arch"/>', - '</resolve>', - ], - ), - (('GET', ['resolve']), - {'allowed': (400, 404), 'params': { - 'project': 'base', - 'repository': 'repo', - 'arch': 'arch', - 'package': 'pkg2', - }}, - [ '<resolve>', ' <binary name="pygame" url="http://pkg2.prm" arch="arch"/>', '</resolve>', ], @@ -122,10 +111,7 @@ class ObsTest(tests.Test): ('http://pkg2-2.prm', ['4']), ])) - obs.presolve({ - 'Debian': {'binary': [['deb']]}, - 'Fedora': {'binary': [['pkg1', 'pkg2']], 'devel': [['pkg3']]}, - }, '.') + obs.presolve(None, ['pkg1', 'pkg2'], '.') self.assertEqual({ 'arch': [ diff --git a/tests/units/node/stats_node.py b/tests/units/node/stats_node.py deleted file mode 100755 index eab7fb8..0000000 --- a/tests/units/node/stats_node.py +++ /dev/null @@ -1,338 +0,0 @@ -#!/usr/bin/env python -# sugar-lint: disable - -import time - -from __init__ import tests - -from sugar_network import db, model -from sugar_network.node.stats_node import Sniffer, stats_node_step -from sugar_network.toolkit.rrd import Rrd -from sugar_network.toolkit.router import Request - - -class StatsTest(tests.Test): - - def test_InitializeTotals(self): - volume = db.Volume('local', model.RESOURCES) - - stats = Sniffer(volume, 'stats/node') - self.assertEqual(0, stats._stats['user']['total']) - self.assertEqual(0, stats._stats['context']['total']) - self.assertEqual(0, stats._stats['post']['total']) - - volume['user'].create({'guid': 'user', 'name': 'user', 'pubkey': ''}) - volume['context'].create({'guid': 'context', 'type': 'activity', 'title': '', 'summary': '', 'description': ''}) - volume['post'].create({'guid': 'post', 'context': 'context', 'title': '', 'message': '', 'type': 'update'}) - - stats = Sniffer(volume, 'stats/node') - self.assertEqual(1, stats._stats['user']['total']) - self.assertEqual(1, stats._stats['context']['total']) - self.assertEqual(1, stats._stats['post']['total']) - - def test_POSTs(self): - volume = db.Volume('local', model.RESOURCES) - stats = Sniffer(volume, 'stats/node') - - request = Request(method='POST', path=['context']) - request.principal = 'user' - stats.log(request) - stats.log(request) - stats.log(request) - self.assertEqual(3, stats._stats['context']['total']) - - def test_DELETEs(self): - volume = db.Volume('local', model.RESOURCES) - stats = Sniffer(volume, 'stats/node') - - request = Request(method='DELETE', path=['context']) - request.principal = 'user' - stats.log(request) - stats.log(request) - stats.log(request) - self.assertEqual(-3, stats._stats['context']['total']) - - def test_Posts(self): - volume = db.Volume('local', model.RESOURCES) - stats = Sniffer(volume, 'stats/node') - volume['context'].create({'guid': 'context', 'type': 'activity', 'title': '', 'summary': '', 'description': ''}) - volume['post'].create({'guid': 'topic', 'type': 'update', 'context': 'context', 'title': '', 'message': ''}) - - request = Request(method='POST', path=['post']) - request.principal = 'user' - request.content = {'context': 'context', 'vote': 1, 'type': 'review', 'title': '', 'message': ''} - stats.log(request) - self.assertEqual(1, stats._stats['post']['total']) - - request = Request(method='POST', path=['post']) - request.principal = 'user' - request.content = {'context': 'context', 'vote': 2, 'type': 'review', 'title': '', 'message': ''} - stats.log(request) - self.assertEqual(2, stats._stats['post']['total']) - - request = Request(method='POST', path=['post']) - request.principal = 'user' - request.content = {'topic': 'topic', 'vote': 3, 'type': 'feedback', 'title': '', 'message': ''} - stats.log(request) - self.assertEqual(3, stats._stats['post']['total']) - - stats.commit_objects() - self.assertEqual([2, 3], volume['context'].get('context')['rating']) - self.assertEqual([1, 3], volume['post'].get('topic')['rating']) - - def test_ContextDownloaded(self): - volume = db.Volume('local', model.RESOURCES) - stats = Sniffer(volume, 'stats/node') - volume['context'].create({'guid': 'context', 'type': 'activity', 'title': '', 'summary': '', 'description': ''}) - volume['release'].create({'guid': 'release', 'context': 'context', 'license': 'GPLv3', 'version': '1', 'date': 0, 'stability': 'stable', 'notes': ''}) - - request = Request(method='GET', path=['release', 'release', 'fake']) - request.principal = 'user' - stats.log(request) - self.assertEqual(0, stats._stats['context']['downloaded']) - - request = Request(method='GET', path=['release', 'release', 'data']) - request.principal = 'user' - stats.log(request) - self.assertEqual(1, stats._stats['context']['downloaded']) - - def test_ContextReleased(self): - volume = db.Volume('local', model.RESOURCES) - stats = Sniffer(volume, 'stats/node') - volume['context'].create({'guid': 'context', 'type': 'activity', 'title': '', 'summary': '', 'description': ''}) - - request = Request(method='POST', path=['release']) - request.principal = 'user' - request.content = {'context': 'context'} - stats.log(request) - self.assertEqual(1, stats._stats['context']['released']) - - def test_ContextFailed(self): - volume = db.Volume('local', model.RESOURCES) - stats = Sniffer(volume, 'stats/node') - volume['context'].create({'guid': 'context', 'type': 'activity', 'title': '', 'summary': '', 'description': ''}) - - request = Request(method='POST', path=['report']) - request.principal = 'user' - request.content = {'context': 'context'} - stats.log(request) - self.assertEqual(1, stats._stats['context']['failed']) - - def test_PostDownloaded(self): - volume = db.Volume('local', model.RESOURCES) - stats = Sniffer(volume, 'stats/node') - volume['post'].create({'guid': 'topic', 'type': 'object', 'context': 'context', 'title': '', 'message': ''}) - - request = Request(method='GET', path=['post', 'topic', 'fake']) - request.principal = 'user' - stats.log(request) - self.assertEqual(0, stats._stats['post']['downloaded']) - - request = Request(method='GET', path=['post', 'topic', 'data']) - request.principal = 'user' - stats.log(request) - self.assertEqual(1, stats._stats['post']['downloaded']) - - def test_Commit(self): - volume = db.Volume('local', model.RESOURCES) - volume['user'].create({'guid': 'user', 'name': 'user', 'pubkey': ''}) - volume['context'].create({'guid': 'context', 'type': 'activity', 'title': '', 'summary': '', 'description': ''}) - volume['post'].create({'guid': 'review', 'context': 'context', 'type': 'review', 'title': '', 'message': '', 'vote': 5}) - - stats = Sniffer(volume, 'stats/node') - request = Request(method='GET', path=['user', 'user']) - request.principal = 'user' - stats.log(request) - request = Request(method='GET', path=['context', 'context']) - request.principal = 'user' - stats.log(request) - request = Request(method='GET', path=['post', 'review']) - request.principal = 'user' - stats.log(request) - - self.assertEqual(1, stats._stats['user']['total']) - self.assertEqual(1, stats._stats['context']['total']) - self.assertEqual(1, stats._stats['post']['total']) - - ts = int(time.time()) - stats.commit(ts) - stats.commit_objects() - - self.assertEqual(1, stats._stats['user']['total']) - self.assertEqual(1, stats._stats['context']['total']) - self.assertEqual(1, stats._stats['post']['total']) - - self.assertEqual([ - [('post', ts, { - 'downloaded': 0.0, - 'total': 1.0, - })], - [('user', ts, { - 'total': 1.0, - })], - [('context', ts, { - 'failed': 0.0, - 'downloaded': 0.0, - 'total': 1.0, - 'released': 0.0, - })], - ], - [[(j.name,) + i for i in j.get(j.last, j.last)] for j in Rrd('stats/node', 1)]) - - def test_CommitContextStats(self): - volume = db.Volume('local', model.RESOURCES) - - volume['context'].create({'guid': 'context', 'type': 'activity', 'title': '', 'summary': '', 'description': ''}) - volume['release'].create({'guid': 'release', 'context': 'context', 'license': 'GPLv3', 'version': '1', 'date': 0, 'stability': 'stable', 'notes': ''}) - - self.assertEqual(0, volume['context'].get('context')['downloads']) - self.assertEqual([0, 0], volume['context'].get('context')['rating']) - - stats = Sniffer(volume, 'stats/node') - request = Request(method='GET', path=['release', 'release', 'data']) - request.principal = 'user' - stats.log(request) - request = Request(method='POST', path=['post']) - request.principal = 'user' - request.content = {'context': 'context', 'vote': 5, 'type': 'review', 'title': '', 'message': ''} - stats.log(request) - - stats.commit() - stats.commit_objects() - - self.assertEqual(1, volume['context'].get('context')['downloads']) - self.assertEqual([1, 5], volume['context'].get('context')['rating']) - - stats.commit() - stats.commit_objects() - - self.assertEqual(1, volume['context'].get('context')['downloads']) - self.assertEqual([1, 5], volume['context'].get('context')['rating']) - - stats = Sniffer(volume, 'stats/node') - request = Request(method='GET', path=['release', 'release', 'data']) - request.principal = 'user' - stats.log(request) - request = Request(method='POST', path=['post']) - request.principal = 'user' - request.content = {'context': 'context', 'vote': 1, 'type': 'review', 'title': '', 'message': ''} - stats.log(request) - stats.commit() - stats.commit_objects() - - self.assertEqual(2, volume['context'].get('context')['downloads']) - self.assertEqual([2, 6], volume['context'].get('context')['rating']) - - def test_CommitTopicStats(self): - volume = db.Volume('local', model.RESOURCES) - - volume['context'].create({'guid': 'context', 'type': 'activity', 'title': '', 'summary': '', 'description': ''}) - volume['post'].create({'guid': 'topic', 'type': 'object', 'context': 'context', 'title': '', 'message': ''}) - - self.assertEqual(0, volume['post'].get('topic')['downloads']) - self.assertEqual([0, 0], volume['post'].get('topic')['rating']) - - stats = Sniffer(volume, 'stats/node') - request = Request(method='GET', path=['post', 'topic', 'data']) - request.principal = 'user' - stats.log(request) - request = Request(method='POST', path=['post']) - request.principal = 'user' - request.content = {'topic': 'topic', 'vote': 5, 'type': 'feedback'} - stats.log(request) - stats.commit() - stats.commit_objects() - - self.assertEqual(1, volume['post'].get('topic')['downloads']) - self.assertEqual([1, 5], volume['post'].get('topic')['rating']) - - stats.commit() - stats.commit_objects() - - self.assertEqual(1, volume['post'].get('topic')['downloads']) - self.assertEqual([1, 5], volume['post'].get('topic')['rating']) - - request = Request(method='GET', path=['post', 'topic', 'data']) - request.principal = 'user' - stats.log(request) - request = Request(method='POST', path=['post']) - request.principal = 'user' - request.content = {'topic': 'topic', 'vote': 1, 'type': 'feedback'} - stats.log(request) - stats.commit() - stats.commit_objects() - - self.assertEqual(2, volume['post'].get('topic')['downloads']) - self.assertEqual([2, 6], volume['post'].get('topic')['rating']) - - def test_Suspend(self): - stats_node_step.value = 5 - volume = db.Volume('local', model.RESOURCES) - volume['context'].create({'guid': 'context', 'type': 'activity', 'title': '', 'summary': '', 'description': ''}) - volume['release'].create({'guid': 'impl', 'context': 'context', 'license': 'GPLv3', 'version': '1', 'date': 0, 'stability': 'stable', 'notes': ''}) - - ts = self.ts = 1000000000 - self.override(time, 'time', lambda: self.ts) - - stats = Sniffer(volume, 'stats') - request = Request(method='POST', path=['context']) - stats.log(request) - request = Request(method='GET', path=['release', 'impl', 'data'], context='context') - stats.log(request) - stats.suspend() - - rdb = Rrd('stats', 1)['context'] - self.assertEqual([ - ], - [i for i in rdb.get(ts, ts + 10)]) - - stats = Sniffer(volume, 'stats') - stats.suspend() - - rdb = Rrd('stats', 1)['context'] - self.assertEqual([ - ], - [i for i in rdb.get(ts, ts + 10)]) - - self.ts += 6 - stats = Sniffer(volume, 'stats') - - rdb = Rrd('stats', 1)['context'] - self.assertEqual([ - (ts + 0, {'failed': 0.0, 'downloaded': 0.0, 'total': 0.0, 'released': 0.0}), - (ts + 5, {'failed': 0.0, 'downloaded': 1.0, 'total': 2.0, 'released': 0.0}), - ], - [i for i in rdb.get(ts, ts + 20)]) - - request = Request(method='POST', path=['context']) - stats.log(request) - request = Request(method='GET', path=['release', 'impl', 'data'], context='context') - stats.log(request) - request = Request(method='GET', path=['release', 'impl', 'data'], context='context') - stats.log(request) - stats.suspend() - - stats = Sniffer(volume, 'stats') - stats.suspend() - - rdb = Rrd('stats', 1)['context'] - self.assertEqual([ - (ts + 0, {'failed': 0.0, 'downloaded': 0.0, 'total': 0.0, 'released': 0.0}), - (ts + 5, {'failed': 0.0, 'downloaded': 1.0, 'total': 2.0, 'released': 0.0}), - ], - [i for i in rdb.get(ts, ts + 10)]) - - self.ts += 6 - stats = Sniffer(volume, 'stats') - - rdb = Rrd('stats', 1)['context'] - self.assertEqual([ - (ts + 0, {'failed': 0.0, 'downloaded': 0.0, 'total': 0.0, 'released': 0.0}), - (ts + 5, {'failed': 0.0, 'downloaded': 1.0, 'total': 2.0, 'released': 0.0}), - (ts + 10, {'failed': 0.0, 'downloaded': 3.0, 'total': 3.0, 'released': 0.0}), - ], - [i for i in rdb.get(ts, ts + 20)]) - - -if __name__ == '__main__': - tests.main() diff --git a/tests/units/node/sync_online.py b/tests/units/node/sync_online.py index 7ee6dcb..e2c864a 100755 --- a/tests/units/node/sync_online.py +++ b/tests/units/node/sync_online.py @@ -44,11 +44,11 @@ class SyncOnlineTest(tests.Test): def type(self, value): return value - @db.indexed_property(slot=1, prefix='N', full_text=True, localized=True) + @db.indexed_property(db.Localized, slot=1, prefix='N', full_text=True) def title(self, value): return value - @db.indexed_property(prefix='D', full_text=True, localized=True) + @db.indexed_property(db.Localized, prefix='D', full_text=True) def message(self, value): return value @@ -80,8 +80,8 @@ class SyncOnlineTest(tests.Test): self.assertEqual([[4, None]], json.load(file('slave/pull.sequence'))) self.assertEqual([[2, None]], json.load(file('slave/push.sequence'))) - guid1 = client.post(['document'], {'context': '', 'message': '1', 'title': '', 'type': 'comment'}) - guid2 = client.post(['document'], {'context': '', 'message': '2', 'title': '', 'type': 'comment'}) + guid1 = client.post(['document'], {'context': '', 'message': '1', 'title': '', 'type': 'post'}) + guid2 = client.post(['document'], {'context': '', 'message': '2', 'title': '', 'type': 'post'}) client.post(cmd='online-sync') self.assertEqual([ @@ -92,7 +92,7 @@ class SyncOnlineTest(tests.Test): self.assertEqual([[6, None]], json.load(file('slave/pull.sequence'))) self.assertEqual([[4, None]], json.load(file('slave/push.sequence'))) - guid3 = client.post(['document'], {'context': '', 'message': '3', 'title': '', 'type': 'comment'}) + guid3 = client.post(['document'], {'context': '', 'message': '3', 'title': '', 'type': 'post'}) client.post(cmd='online-sync') self.assertEqual([ {'guid': guid1, 'message': {'en-us': '1'}}, @@ -128,7 +128,7 @@ class SyncOnlineTest(tests.Test): client.put(['document', guid1], {'message': 'a'}) client.put(['document', guid2], {'message': 'b'}) client.put(['document', guid3], {'message': 'c'}) - guid4 = client.post(['document'], {'context': '', 'message': 'd', 'title': '', 'type': 'comment'}) + guid4 = client.post(['document'], {'context': '', 'message': 'd', 'title': '', 'type': 'post'}) client.delete(['document', guid2]) client.post(cmd='online-sync') self.assertEqual([ @@ -158,8 +158,8 @@ class SyncOnlineTest(tests.Test): self.assertEqual([[4, None]], json.load(file('slave/pull.sequence'))) self.assertEqual([[2, None]], json.load(file('slave/push.sequence'))) - guid1 = client.post(['document'], {'context': '', 'message': '1', 'title': '', 'type': 'comment'}) - guid2 = client.post(['document'], {'context': '', 'message': '2', 'title': '', 'type': 'comment'}) + guid1 = client.post(['document'], {'context': '', 'message': '1', 'title': '', 'type': 'post'}) + guid2 = client.post(['document'], {'context': '', 'message': '2', 'title': '', 'type': 'post'}) slave_client.post(cmd='online-sync') self.assertEqual([ @@ -170,7 +170,7 @@ class SyncOnlineTest(tests.Test): self.assertEqual([[6, None]], json.load(file('slave/pull.sequence'))) self.assertEqual([[2, None]], json.load(file('slave/push.sequence'))) - guid3 = client.post(['document'], {'context': '', 'message': '3', 'title': '', 'type': 'comment'}) + guid3 = client.post(['document'], {'context': '', 'message': '3', 'title': '', 'type': 'post'}) slave_client.post(cmd='online-sync') self.assertEqual([ {'guid': guid1, 'message': {'en-us': '1'}}, @@ -206,7 +206,7 @@ class SyncOnlineTest(tests.Test): client.put(['document', guid1], {'message': 'a'}) client.put(['document', guid2], {'message': 'b'}) client.put(['document', guid3], {'message': 'c'}) - guid4 = client.post(['document'], {'context': '', 'message': 'd', 'title': '', 'type': 'comment'}) + guid4 = client.post(['document'], {'context': '', 'message': 'd', 'title': '', 'type': 'post'}) client.delete(['document', guid2]) slave_client.post(cmd='online-sync') self.assertEqual([ @@ -252,7 +252,7 @@ class SyncOnlineTest(tests.Test): self.assertEqual([[4, None]], json.load(file('slave/pull.sequence'))) self.assertEqual([[2, None]], json.load(file('slave/push.sequence'))) - guid = slave.post(['document'], {'context': '', 'message': '1', 'title': '1', 'type': 'comment'}) + guid = slave.post(['document'], {'context': '', 'message': '1', 'title': '1', 'type': 'post'}) slave.post(cmd='online-sync') coroutine.sleep(1) diff --git a/tests/units/node/volume.py b/tests/units/node/volume.py deleted file mode 100755 index 01e71a7..0000000 --- a/tests/units/node/volume.py +++ /dev/null @@ -1,826 +0,0 @@ -#!/usr/bin/env python -# sugar-lint: disable - -import os -import time -import urllib2 -import hashlib -from cStringIO import StringIO - -from __init__ import tests - -from sugar_network import db, toolkit, model -from sugar_network.node.volume import diff, merge -from sugar_network.node.stats_node import Sniffer -from sugar_network.node.routes import NodeRoutes -from sugar_network.toolkit.rrd import Rrd -from sugar_network.toolkit.router import Router, Request, Response, fallbackroute, Blob, ACL, route - - -current_time = time.time - - -class VolumeTest(tests.Test): - - def setUp(self): - tests.Test.setUp(self) - self.override(time, 'time', lambda: 0) - self.override(NodeRoutes, 'authorize', lambda self, user, role: True) - - def test_diff(self): - - class Document(db.Resource): - - @db.indexed_property(slot=1) - def prop(self, value): - return value - - volume = db.Volume('db', [Document]) - cp = NodeRoutes('guid', volume) - - guid1 = call(cp, method='POST', document='document', content={'prop': 'a'}) - self.utime('db/document/%s/%s' % (guid1[:2], guid1), 1) - guid2 = call(cp, method='POST', document='document', content={'prop': 'b'}) - self.utime('db/document/%s/%s' % (guid2[:2], guid2), 2) - - in_seq = toolkit.Sequence([[1, None]]) - self.assertEqual([ - {'resource': 'document'}, - {'guid': guid1, - 'diff': { - 'guid': {'value': guid1, 'mtime': 1}, - 'mtime': {'value': 0, 'mtime': 1}, - 'ctime': {'value': 0, 'mtime': 1}, - 'prop': {'value': 'a', 'mtime': 1}, - 'author': {'mtime': 1, 'value': {}}, - 'layer': {'mtime': 1, 'value': []}, - 'tags': {'mtime': 1, 'value': []}, - }, - }, - {'guid': guid2, - 'diff': { - 'guid': {'value': guid2, 'mtime': 2}, - 'mtime': {'value': 0, 'mtime': 2}, - 'ctime': {'value': 0, 'mtime': 2}, - 'prop': {'value': 'b', 'mtime': 2}, - 'author': {'mtime': 2, 'value': {}}, - 'layer': {'mtime': 2, 'value': []}, - 'tags': {'mtime': 2, 'value': []}, - }, - }, - {'commit': [[1, 2]]}, - ], - [i for i in diff(volume, in_seq)]) - self.assertEqual([[1, None]], in_seq) - - def test_diff_Partial(self): - - class Document(db.Resource): - - @db.indexed_property(slot=1) - def prop(self, value): - return value - - volume = db.Volume('db', [Document]) - cp = NodeRoutes('guid', volume) - - guid1 = call(cp, method='POST', document='document', content={'prop': 'a'}) - self.utime('db/document/%s/%s' % (guid1[:2], guid1), 1) - guid2 = call(cp, method='POST', document='document', content={'prop': 'b'}) - self.utime('db/document/%s/%s' % (guid2[:2], guid2), 2) - - in_seq = toolkit.Sequence([[1, None]]) - patch = diff(volume, in_seq) - self.assertEqual({'resource': 'document'}, next(patch)) - self.assertEqual(guid1, next(patch)['guid']) - self.assertEqual({'commit': []}, patch.throw(StopIteration())) - try: - next(patch) - assert False - except StopIteration: - pass - - patch = diff(volume, in_seq) - self.assertEqual({'resource': 'document'}, next(patch)) - self.assertEqual(guid1, next(patch)['guid']) - self.assertEqual(guid2, next(patch)['guid']) - self.assertEqual({'commit': [[1, 1]]}, patch.throw(StopIteration())) - try: - next(patch) - assert False - except StopIteration: - pass - - def test_diff_Stretch(self): - - class Document(db.Resource): - - @db.indexed_property(slot=1) - def prop(self, value): - return value - - volume = db.Volume('db', [Document]) - cp = NodeRoutes('guid', volume) - - guid1 = call(cp, method='POST', document='document', content={'prop': 'a'}) - self.utime('db/document/%s/%s' % (guid1[:2], guid1), 1) - guid2 = call(cp, method='POST', document='document', content={'prop': 'b'}) - volume['document'].delete(guid2) - guid3 = call(cp, method='POST', document='document', content={'prop': 'c'}) - self.utime('db/document/%s/%s' % (guid3[:2], guid3), 2) - guid4 = call(cp, method='POST', document='document', content={'prop': 'd'}) - volume['document'].delete(guid4) - guid5 = call(cp, method='POST', document='document', content={'prop': 'f'}) - self.utime('db/document/%s/%s' % (guid5[:2], guid5), 2) - - in_seq = toolkit.Sequence([[1, None]]) - patch = diff(volume, in_seq) - self.assertEqual({'resource': 'document'}, patch.send(None)) - self.assertEqual(guid1, patch.send(None)['guid']) - self.assertEqual(guid3, patch.send(None)['guid']) - self.assertEqual(guid5, patch.send(None)['guid']) - self.assertEqual({'commit': [[1, 1], [3, 3]]}, patch.throw(StopIteration())) - try: - patch.send(None) - assert False - except StopIteration: - pass - - patch = diff(volume, in_seq) - self.assertEqual({'resource': 'document'}, patch.send(None)) - self.assertEqual(guid1, patch.send(None)['guid']) - self.assertEqual(guid3, patch.send(None)['guid']) - self.assertEqual(guid5, patch.send(None)['guid']) - self.assertEqual({'commit': [[1, 5]]}, patch.send(None)) - try: - patch.send(None) - assert False - except StopIteration: - pass - - def test_diff_DoNotStretchContinuesPacket(self): - - class Document(db.Resource): - - @db.indexed_property(slot=1) - def prop(self, value): - return value - - volume = db.Volume('db', [Document]) - cp = NodeRoutes('guid', volume) - - guid1 = call(cp, method='POST', document='document', content={'prop': 'a'}) - volume['document'].delete(guid1) - guid2 = call(cp, method='POST', document='document', content={'prop': 'b'}) - volume['document'].delete(guid2) - guid3 = call(cp, method='POST', document='document', content={'prop': 'c'}) - self.utime('db/document/%s/%s' % (guid3[:2], guid3), 2) - guid4 = call(cp, method='POST', document='document', content={'prop': 'd'}) - volume['document'].delete(guid4) - guid5 = call(cp, method='POST', document='document', content={'prop': 'f'}) - self.utime('db/document/%s/%s' % (guid5[:2], guid5), 2) - - in_seq = toolkit.Sequence([[1, None]]) - patch = diff(volume, in_seq, toolkit.Sequence([[1, 1]])) - self.assertEqual({'resource': 'document'}, patch.send(None)) - self.assertEqual(guid3, patch.send(None)['guid']) - self.assertEqual(guid5, patch.send(None)['guid']) - self.assertEqual({'commit': [[1, 1], [3, 3], [5, 5]]}, patch.send(None)) - try: - patch.send(None) - assert False - except StopIteration: - pass - - def test_diff_TheSameInSeqForAllDocuments(self): - - class Document1(db.Resource): - pass - - class Document2(db.Resource): - pass - - class Document3(db.Resource): - pass - - volume = db.Volume('db', [Document1, Document2, Document3]) - cp = NodeRoutes('guid', volume) - - guid3 = call(cp, method='POST', document='document1', content={}) - self.utime('db/document/%s/%s' % (guid3[:2], guid3), 3) - guid2 = call(cp, method='POST', document='document2', content={}) - self.utime('db/document/%s/%s' % (guid2[:2], guid2), 2) - guid1 = call(cp, method='POST', document='document3', content={}) - self.utime('db/document/%s/%s' % (guid1[:2], guid1), 1) - - in_seq = toolkit.Sequence([[1, None]]) - patch = diff(volume, in_seq) - self.assertEqual({'resource': 'document1'}, patch.send(None)) - self.assertEqual(guid3, patch.send(None)['guid']) - self.assertEqual({'resource': 'document2'}, patch.send(None)) - self.assertEqual(guid2, patch.send(None)['guid']) - self.assertEqual({'resource': 'document3'}, patch.send(None)) - self.assertEqual(guid1, patch.send(None)['guid']) - self.assertEqual({'commit': [[1, 3]]}, patch.send(None)) - try: - patch.send(None) - assert False - except StopIteration: - pass - - def test_merge_Create(self): - - class Document1(db.Resource): - - @db.indexed_property(slot=1) - def prop(self, value): - return value - - class Document2(db.Resource): - pass - - self.touch(('db/seqno', '100')) - volume = db.Volume('db', [Document1, Document2]) - - records = [ - {'resource': 'document1'}, - {'guid': '1', 'diff': { - 'guid': {'value': '1', 'mtime': 1.0}, - 'ctime': {'value': 2, 'mtime': 2.0}, - 'mtime': {'value': 3, 'mtime': 3.0}, - 'prop': {'value': '4', 'mtime': 4.0}, - }}, - {'resource': 'document2'}, - {'guid': '5', 'diff': { - 'guid': {'value': '5', 'mtime': 5.0}, - 'ctime': {'value': 6, 'mtime': 6.0}, - 'mtime': {'value': 7, 'mtime': 7.0}, - }}, - {'commit': [[1, 2]]}, - ] - self.assertEqual(([[1, 2]], [[101, 102]]), merge(volume, records)) - - self.assertEqual( - {'guid': '1', 'prop': '4', 'ctime': 2, 'mtime': 3}, - volume['document1'].get('1').properties(['guid', 'ctime', 'mtime', 'prop'])) - self.assertEqual(1, os.stat('db/document1/1/1/guid').st_mtime) - self.assertEqual(2, os.stat('db/document1/1/1/ctime').st_mtime) - self.assertEqual(3, os.stat('db/document1/1/1/mtime').st_mtime) - self.assertEqual(4, os.stat('db/document1/1/1/prop').st_mtime) - - self.assertEqual( - {'guid': '5', 'ctime': 6, 'mtime': 7}, - volume['document2'].get('5').properties(['guid', 'ctime', 'mtime'])) - self.assertEqual(5, os.stat('db/document2/5/5/guid').st_mtime) - self.assertEqual(6, os.stat('db/document2/5/5/ctime').st_mtime) - self.assertEqual(7, os.stat('db/document2/5/5/mtime').st_mtime) - - def test_merge_Update(self): - - class Document(db.Resource): - - @db.indexed_property(slot=1) - def prop(self, value): - return value - - self.touch(('db/seqno', '100')) - volume = db.Volume('db', [Document]) - volume['document'].create({'guid': '1', 'prop': '1', 'ctime': 1, 'mtime': 1}) - for i in os.listdir('db/document/1/1'): - os.utime('db/document/1/1/%s' % i, (2, 2)) - - records = [ - {'resource': 'document'}, - {'guid': '1', 'diff': {'prop': {'value': '2', 'mtime': 1.0}}}, - {'commit': [[1, 1]]}, - ] - self.assertEqual(([[1, 1]], []), merge(volume, records)) - self.assertEqual( - {'guid': '1', 'prop': '1', 'ctime': 1, 'mtime': 1}, - volume['document'].get('1').properties(['guid', 'ctime', 'mtime', 'prop'])) - self.assertEqual(2, os.stat('db/document/1/1/prop').st_mtime) - - records = [ - {'resource': 'document'}, - {'guid': '1', 'diff': {'prop': {'value': '3', 'mtime': 2.0}}}, - {'commit': [[2, 2]]}, - ] - self.assertEqual(([[2, 2]], []), merge(volume, records)) - self.assertEqual( - {'guid': '1', 'prop': '1', 'ctime': 1, 'mtime': 1}, - volume['document'].get('1').properties(['guid', 'ctime', 'mtime', 'prop'])) - self.assertEqual(2, os.stat('db/document/1/1/prop').st_mtime) - - records = [ - {'resource': 'document'}, - {'guid': '1', 'diff': {'prop': {'value': '4', 'mtime': 3.0}}}, - {'commit': [[3, 3]]}, - ] - self.assertEqual(([[3, 3]], [[102, 102]]), merge(volume, records)) - self.assertEqual( - {'guid': '1', 'prop': '4', 'ctime': 1, 'mtime': 1}, - volume['document'].get('1').properties(['guid', 'ctime', 'mtime', 'prop'])) - self.assertEqual(3, os.stat('db/document/1/1/prop').st_mtime) - - def test_merge_MultipleCommits(self): - - class Document(db.Resource): - - @db.stored_property() - def prop(self, value): - return value - - self.touch(('db/seqno', '100')) - volume = db.Volume('db', [Document]) - - def generator(): - for i in [ - {'resource': 'document'}, - {'commit': [[1, 1]]}, - {'guid': '1', 'diff': { - 'guid': {'value': '1', 'mtime': 1.0}, - 'ctime': {'value': 2, 'mtime': 2.0}, - 'mtime': {'value': 3, 'mtime': 3.0}, - 'prop': {'value': '4', 'mtime': 4.0}, - }}, - {'commit': [[2, 3]]}, - ]: - yield i - - records = generator() - self.assertEqual(([[1, 3]], [[101, 101]]), merge(volume, records)) - assert volume['document'].exists('1') - - def test_merge_UpdateStats(self): - volume = db.Volume('db', model.RESOURCES) - cp = NodeRoutes('guid', volume) - stats = Sniffer(volume, 'stats/node') - - records = [ - {'resource': 'context'}, - {'guid': 'context', 'diff': { - 'guid': {'value': 'context', 'mtime': 1.0}, - 'ctime': {'value': 1, 'mtime': 1.0}, - 'mtime': {'value': 1, 'mtime': 1.0}, - 'type': {'value': ['package'], 'mtime': 1.0}, - 'title': {'value': {}, 'mtime': 1.0}, - 'summary': {'value': {}, 'mtime': 1.0}, - 'description': {'value': {}, 'mtime': 1.0}, - }}, - {'resource': 'post'}, - {'guid': 'topic_1', 'diff': { - 'guid': {'value': 'topic_1', 'mtime': 1.0}, - 'ctime': {'value': 1, 'mtime': 1.0}, - 'mtime': {'value': 1, 'mtime': 1.0}, - 'type': {'value': 'object', 'mtime': 1.0}, - 'context': {'value': 'context', 'mtime': 1.0}, - 'title': {'value': {}, 'mtime': 1.0}, - 'message': {'value': {}, 'mtime': 1.0}, - 'solution': {'value': 'solution_1', 'mtime': 1.0}, - }}, - {'guid': 'topic_2', 'diff': { - 'guid': {'value': 'topic_2', 'mtime': 1.0}, - 'ctime': {'value': 1, 'mtime': 1.0}, - 'mtime': {'value': 1, 'mtime': 1.0}, - 'type': {'value': 'object', 'mtime': 1.0}, - 'context': {'value': 'context', 'mtime': 1.0}, - 'title': {'value': {}, 'mtime': 1.0}, - 'message': {'value': {}, 'mtime': 1.0}, - 'solution': {'value': 'solution_2', 'mtime': 1.0}, - }}, - {'guid': 'context_review', 'diff': { - 'guid': {'value': 'context_review', 'mtime': 1.0}, - 'ctime': {'value': 1, 'mtime': 1.0}, - 'mtime': {'value': 1, 'mtime': 1.0}, - 'context': {'value': 'context', 'mtime': 1.0}, - 'vote': {'value': 1, 'mtime': 1.0}, - 'author': {'mtime': 1, 'value': {}}, - 'layer': {'mtime': 1, 'value': []}, - 'tags': {'mtime': 1, 'value': []}, - 'type': {'value': 'review', 'mtime': 1.0}, - }}, - {'guid': 'topic_review', 'diff': { - 'guid': {'value': 'topic_review', 'mtime': 1.0}, - 'ctime': {'value': 1, 'mtime': 1.0}, - 'mtime': {'value': 1, 'mtime': 1.0}, - 'context': {'value': 'context', 'mtime': 1.0}, - 'topic': {'value': 'topic_1', 'mtime': 1.0}, - 'vote': {'value': 1, 'mtime': 1.0}, - 'author': {'mtime': 1, 'value': {}}, - 'layer': {'mtime': 1, 'value': []}, - 'tags': {'mtime': 1, 'value': []}, - 'type': {'value': 'feedback', 'mtime': 1.0}, - }}, - {'guid': 'solution_1', 'diff': { - 'guid': {'value': 'solution_1', 'mtime': 1.0}, - 'ctime': {'value': 1, 'mtime': 1.0}, - 'mtime': {'value': 1, 'mtime': 1.0}, - 'context': {'value': 'context', 'mtime': 1.0}, - 'topic': {'value': 'topic_1', 'mtime': 1.0}, - 'type': {'value': 'answer', 'mtime': 1.0}, - 'title': {'value': {}, 'mtime': 1.0}, - 'message': {'value': {}, 'mtime': 1.0}, - }}, - {'guid': 'solution_2', 'diff': { - 'guid': {'value': 'solution_2', 'mtime': 1.0}, - 'ctime': {'value': 1, 'mtime': 1.0}, - 'mtime': {'value': 1, 'mtime': 1.0}, - 'context': {'value': 'context', 'mtime': 1.0}, - 'topic': {'value': 'topic_2', 'mtime': 1.0}, - 'type': {'value': 'answer', 'mtime': 1.0}, - 'title': {'value': {}, 'mtime': 1.0}, - 'message': {'value': {}, 'mtime': 1.0}, - }}, - {'resource': 'release'}, - {'guid': 'release', 'diff': { - 'guid': {'value': 'release', 'mtime': 1.0}, - 'ctime': {'value': 1, 'mtime': 1.0}, - 'mtime': {'value': 1, 'mtime': 1.0}, - 'context': {'value': 'context', 'mtime': 1.0}, - 'license': {'value': ['GPL-3.0'], 'mtime': 1.0}, - 'version': {'value': '1', 'mtime': 1.0}, - 'stability': {'value': 'stable', 'mtime': 1.0}, - 'notes': {'value': {}, 'mtime': 1.0}, - }}, - {'commit': [[1, 1]]}, - ] - merge(volume, records, stats=stats) - ts = int(current_time()) - stats.commit(ts) - stats.commit_objects() - - self.assertEqual([ - [('post', ts, { - 'downloaded': 0.0, - 'total': 6.0, - })], - [('user', ts, { - 'total': 0.0, - })], - [('context', ts, { - 'failed': 0.0, - 'downloaded': 0.0, - 'total': 1.0, - 'released': 1.0, - })], - ], - [[(j.name,) + i for i in j.get(j.last, j.last)] for j in Rrd('stats/node', 1)]) - self.assertEqual([1, 1], volume['context'].get('context')['rating']) - self.assertEqual([1, 1], volume['post'].get('topic_1')['rating']) - - records = [ - {'resource': 'post'}, - {'guid': 'topic_2', 'diff': {'solution': {'value': '', 'mtime': 2.0}}}, - {'commit': [[2, 2]]}, - ] - merge(volume, records, stats=stats) - ts += 1 - stats.commit(ts) - stats.commit_objects() - - self.assertEqual([ - [('post', ts, { - 'downloaded': 0.0, - 'total': 6.0, - })], - [('user', ts, { - 'total': 0.0, - })], - [('context', ts, { - 'failed': 0.0, - 'downloaded': 0.0, - 'total': 1.0, - 'released': 1.0, - })], - ], - [[(j.name,) + i for i in j.get(j.last, j.last)] for j in Rrd('stats/node', 1)]) - - records = [ - {'resource': 'context'}, - {'guid': 'context', 'diff': {'layer': {'value': ['deleted'], 'mtime': 3.0}}}, - {'resource': 'post'}, - {'guid': 'topic_1', 'diff': {'layer': {'value': ['deleted'], 'mtime': 3.0}}}, - {'guid': 'topic_2', 'diff': {'layer': {'value': ['deleted'], 'mtime': 3.0}}}, - {'guid': 'context_review', 'diff': {'layer': {'value': ['deleted'], 'mtime': 3.0}}}, - {'guid': 'topic_review', 'diff': {'layer': {'value': ['deleted'], 'mtime': 3.0}}}, - {'guid': 'solution_1', 'diff': {'layer': {'value': ['deleted'], 'mtime': 3.0}}}, - {'guid': 'solution_2', 'diff': {'layer': {'value': ['deleted'], 'mtime': 3.0}}}, - {'resource': 'release'}, - {'guid': 'release', 'diff': {'layer': {'value': ['deleted'], 'mtime': 3.0}}}, - {'commit': [[3, 3]]}, - ] - merge(volume, records, stats=stats) - ts += 1 - stats.commit(ts) - stats.commit_objects() - - self.assertEqual([ - [('post', ts, { - 'downloaded': 0.0, - 'total': 0.0, - })], - [('user', ts, { - 'total': 0.0, - })], - [('context', ts, { - 'failed': 0.0, - 'downloaded': 0.0, - 'total': 0.0, - 'released': 1.0, - })], - ], - [[(j.name,) + i for i in j.get(j.last, j.last)] for j in Rrd('stats/node', 1)]) - - def test_diff_Blobs(self): - - class Document(db.Resource): - - @db.blob_property() - def prop(self, value): - return value - - volume = db.Volume('db', [Document]) - cp = NodeRoutes('guid', volume) - - guid = call(cp, method='POST', document='document', content={}) - call(cp, method='PUT', document='document', guid=guid, content={'prop': 'payload'}) - self.utime('db', 0) - - patch = diff(volume, toolkit.Sequence([[1, None]])) - self.assertEqual( - {'resource': 'document'}, - next(patch)) - record = next(patch) - self.assertEqual('payload', ''.join([i for i in record.pop('blob')])) - self.assertEqual( - {'guid': guid, 'blob_size': len('payload'), 'diff': { - 'prop': { - 'digest': hashlib.sha1('payload').hexdigest(), - 'blob_size': len('payload'), - 'mime_type': 'application/octet-stream', - 'mtime': 0, - }, - }}, - record) - self.assertEqual( - {'guid': guid, 'diff': { - 'guid': {'value': guid, 'mtime': 0}, - 'author': {'mtime': 0, 'value': {}}, - 'layer': {'mtime': 0, 'value': []}, - 'tags': {'mtime': 0, 'value': []}, - 'mtime': {'value': 0, 'mtime': 0}, - 'ctime': {'value': 0, 'mtime': 0}, - }}, - next(patch)) - self.assertEqual( - {'commit': [[1, 2]]}, - next(patch)) - self.assertRaises(StopIteration, next, patch) - - def test_diff_BlobUrls(self): - url = 'http://src.sugarlabs.org/robots.txt' - blob = urllib2.urlopen(url).read() - - class Document(db.Resource): - - @db.blob_property() - def prop(self, value): - return value - - volume = db.Volume('db', [Document]) - cp = NodeRoutes('guid', volume) - - guid = call(cp, method='POST', document='document', content={}) - call(cp, method='PUT', document='document', guid=guid, content={'prop': {'url': url}}) - self.utime('db', 1) - - self.assertEqual([ - {'resource': 'document'}, - {'guid': guid, - 'diff': { - 'guid': {'value': guid, 'mtime': 1}, - 'author': {'mtime': 1, 'value': {}}, - 'layer': {'mtime': 1, 'value': []}, - 'tags': {'mtime': 1, 'value': []}, - 'mtime': {'value': 0, 'mtime': 1}, - 'ctime': {'value': 0, 'mtime': 1}, - 'prop': {'url': url, 'mtime': 1}, - }, - }, - {'commit': [[1, 2]]}, - ], - [i for i in diff(volume, toolkit.Sequence([[1, None]]))]) - - patch = diff(volume, toolkit.Sequence([[1, None]]), fetch_blobs=True) - self.assertEqual( - {'resource': 'document'}, - next(patch)) - record = next(patch) - self.assertEqual(blob, ''.join([i for i in record.pop('blob')])) - self.assertEqual( - {'guid': guid, 'blob_size': len(blob), 'diff': {'prop': {'mtime': 1}}}, - record) - self.assertEqual( - {'guid': guid, 'diff': { - 'guid': {'value': guid, 'mtime': 1}, - 'author': {'mtime': 1, 'value': {}}, - 'layer': {'mtime': 1, 'value': []}, - 'tags': {'mtime': 1, 'value': []}, - 'mtime': {'value': 0, 'mtime': 1}, - 'ctime': {'value': 0, 'mtime': 1}, - }}, - next(patch)) - self.assertEqual( - {'commit': [[1, 2]]}, - next(patch)) - self.assertRaises(StopIteration, next, patch) - - def test_diff_SkipBrokenBlobUrls(self): - - class Document(db.Resource): - - @db.blob_property() - def prop(self, value): - return value - - volume = db.Volume('db', [Document]) - cp = NodeRoutes('guid', volume) - - guid1 = call(cp, method='POST', document='document', content={}) - call(cp, method='PUT', document='document', guid=guid1, content={'prop': {'url': 'http://foo/bar'}}) - guid2 = call(cp, method='POST', document='document', content={}) - self.utime('db', 1) - - self.assertEqual([ - {'resource': 'document'}, - {'guid': guid1, - 'diff': { - 'guid': {'value': guid1, 'mtime': 1}, - 'author': {'mtime': 1, 'value': {}}, - 'layer': {'mtime': 1, 'value': []}, - 'tags': {'mtime': 1, 'value': []}, - 'mtime': {'value': 0, 'mtime': 1}, - 'ctime': {'value': 0, 'mtime': 1}, - 'prop': {'url': 'http://foo/bar', 'mtime': 1}, - }, - }, - {'guid': guid2, - 'diff': { - 'guid': {'value': guid2, 'mtime': 1}, - 'author': {'mtime': 1, 'value': {}}, - 'layer': {'mtime': 1, 'value': []}, - 'tags': {'mtime': 1, 'value': []}, - 'mtime': {'value': 0, 'mtime': 1}, - 'ctime': {'value': 0, 'mtime': 1}, - }, - }, - {'commit': [[1, 3]]}, - ], - [i for i in diff(volume, toolkit.Sequence([[1, None]]), fetch_blobs=False)]) - - self.assertEqual([ - {'resource': 'document'}, - {'guid': guid1, - 'diff': { - 'guid': {'value': guid1, 'mtime': 1}, - 'author': {'mtime': 1, 'value': {}}, - 'layer': {'mtime': 1, 'value': []}, - 'tags': {'mtime': 1, 'value': []}, - 'mtime': {'value': 0, 'mtime': 1}, - 'ctime': {'value': 0, 'mtime': 1}, - }, - }, - {'guid': guid2, - 'diff': { - 'guid': {'value': guid2, 'mtime': 1}, - 'author': {'mtime': 1, 'value': {}}, - 'layer': {'mtime': 1, 'value': []}, - 'tags': {'mtime': 1, 'value': []}, - 'mtime': {'value': 0, 'mtime': 1}, - 'ctime': {'value': 0, 'mtime': 1}, - }, - }, - {'commit': [[1, 3]]}, - ], - [i for i in diff(volume, toolkit.Sequence([[1, None]]), fetch_blobs=True)]) - - def test_merge_Blobs(self): - - class Document(db.Resource): - - @db.blob_property() - def prop(self, value): - return value - - volume = db.Volume('db', [Document]) - - merge(volume, [ - {'resource': 'document'}, - {'guid': '1', 'diff': { - 'guid': {'value': '1', 'mtime': 1.0}, - 'ctime': {'value': 2, 'mtime': 2.0}, - 'mtime': {'value': 3, 'mtime': 3.0}, - 'prop': { - 'blob': StringIO('payload'), - 'blob_size': len('payload'), - 'digest': hashlib.sha1('payload').hexdigest(), - 'mime_type': 'foo/bar', - 'mtime': 1, - }, - }}, - {'commit': [[1, 1]]}, - ]) - - assert volume['document'].exists('1') - blob = volume['document'].get('1')['prop'] - self.assertEqual(1, blob['mtime']) - self.assertEqual('foo/bar', blob['mime_type']) - self.assertEqual(hashlib.sha1('payload').hexdigest(), blob['digest']) - self.assertEqual(tests.tmpdir + '/db/document/1/1/prop.blob', blob['blob']) - self.assertEqual('payload', file(blob['blob']).read()) - - def test_diff_ByLayers(self): - - class Context(db.Resource): - pass - - class release(db.Resource): - pass - - class Review(db.Resource): - pass - - volume = db.Volume('db', [Context, release, Review]) - volume['context'].create({'guid': '0', 'ctime': 1, 'mtime': 1, 'layer': ['layer0', 'common']}) - volume['context'].create({'guid': '1', 'ctime': 1, 'mtime': 1, 'layer': 'layer1'}) - volume['release'].create({'guid': '2', 'ctime': 2, 'mtime': 2, 'layer': 'layer2'}) - volume['review'].create({'guid': '3', 'ctime': 3, 'mtime': 3, 'layer': 'layer3'}) - - volume['context'].update('0', {'tags': '0'}) - volume['context'].update('1', {'tags': '1'}) - volume['release'].update('2', {'tags': '2'}) - volume['review'].update('3', {'tags': '3'}) - self.utime('db', 0) - - self.assertEqual(sorted([ - {'resource': 'context'}, - {'guid': '0', 'diff': {'tags': {'value': '0', 'mtime': 0}}}, - {'guid': '1', 'diff': {'tags': {'value': '1', 'mtime': 0}}}, - {'resource': 'release'}, - {'guid': '2', 'diff': {'tags': {'value': '2', 'mtime': 0}}}, - {'resource': 'review'}, - {'guid': '3', 'diff': {'tags': {'value': '3', 'mtime': 0}}}, - {'commit': [[5, 8]]}, - ]), - sorted([i for i in diff(volume, toolkit.Sequence([[5, None]]))])) - - self.assertEqual(sorted([ - {'resource': 'context'}, - {'guid': '0', 'diff': {'tags': {'value': '0', 'mtime': 0}}}, - {'guid': '1', 'diff': {'tags': {'value': '1', 'mtime': 0}}}, - {'resource': 'release'}, - {'resource': 'review'}, - {'guid': '3', 'diff': {'tags': {'value': '3', 'mtime': 0}}}, - {'commit': [[5, 8]]}, - ]), - sorted([i for i in diff(volume, toolkit.Sequence([[5, None]]), layer='layer1')])) - - self.assertEqual(sorted([ - {'resource': 'context'}, - {'guid': '0', 'diff': {'tags': {'value': '0', 'mtime': 0}}}, - {'resource': 'release'}, - {'guid': '2', 'diff': {'tags': {'value': '2', 'mtime': 0}}}, - {'resource': 'review'}, - {'guid': '3', 'diff': {'tags': {'value': '3', 'mtime': 0}}}, - {'commit': [[5, 8]]}, - ]), - sorted([i for i in diff(volume, toolkit.Sequence([[5, None]]), layer='layer2')])) - - self.assertEqual(sorted([ - {'resource': 'context'}, - {'guid': '0', 'diff': {'tags': {'value': '0', 'mtime': 0}}}, - {'resource': 'release'}, - {'resource': 'review'}, - {'guid': '3', 'diff': {'tags': {'value': '3', 'mtime': 0}}}, - {'commit': [[5, 8]]}, - ]), - sorted([i for i in diff(volume, toolkit.Sequence([[5, None]]), layer='foo')])) - - -def call(routes, method, document=None, guid=None, prop=None, cmd=None, content=None, **kwargs): - path = [] - if document: - path.append(document) - if guid: - path.append(guid) - if prop: - path.append(prop) - request = Request(method=method, path=path, cmd=cmd, content=content) - request.update(kwargs) - request.environ['HTTP_HOST'] = '127.0.0.1' - router = Router(routes) - return router.call(request, Response()) - - -if __name__ == '__main__': - tests.main() diff --git a/tests/units/toolkit/__main__.py b/tests/units/toolkit/__main__.py index 79b0e5b..68cb254 100644 --- a/tests/units/toolkit/__main__.py +++ b/tests/units/toolkit/__main__.py @@ -2,6 +2,7 @@ from __init__ import tests +from coroutine import * from http import * from lsb_release import * from mountpoints import * @@ -11,6 +12,7 @@ from options import * from spec import * from router import * from gbus import * +from i18n import * if __name__ == '__main__': tests.main() diff --git a/tests/units/toolkit/coroutine.py b/tests/units/toolkit/coroutine.py new file mode 100755 index 0000000..95738d0 --- /dev/null +++ b/tests/units/toolkit/coroutine.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python +# sugar-lint: disable + +from __init__ import tests + +from sugar_network.toolkit.coroutine import Spooler, spawn, sleep + + +class CoroutineTest(tests.Test): + + def test_Spooler_ContinuousFeeding(self): + spooler = Spooler() + events = [] + + def consumer(num): + while True: + events[num].append(spooler.wait()) + + for i in range(10): + events.append([]) + spawn(consumer, i) + sleep(.1) + + for i in range(10): + spooler.notify_all(i) + sleep(.1) + self.assertEqual([range(10)] * 10, events) + + +if __name__ == '__main__': + tests.main() diff --git a/tests/units/toolkit/i18n.py b/tests/units/toolkit/i18n.py new file mode 100755 index 0000000..6c4c0ca --- /dev/null +++ b/tests/units/toolkit/i18n.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python +# sugar-lint: disable + +import gettext + +from __init__ import tests + +from sugar_network.toolkit import i18n + + +class I18nTest(tests.Test): + + def test_decode(self): + # Fallback to default lang + i18n._default_langs = ['default'] + self.assertEqual('foo', i18n.decode({'lang': 'foo', 'default': 'bar'}, 'lang')) + self.assertEqual('bar', i18n.decode({'lang': 'foo', 'default': 'bar'}, 'fake')) + + # Exact accept_language + self.assertEqual('', i18n.decode(None, 'lang')) + self.assertEqual('foo', i18n.decode('foo', 'lang')) + self.assertEqual('foo', i18n.decode({'lang': 'foo', 'fake': 'bar', 'default': 'default'}, 'lang')) + self.assertEqual('foo', i18n.decode({'lang': 'foo', 'fake': 'bar', 'default': 'default'}, ['lang', 'fake'])) + self.assertEqual('bar', i18n.decode({'lang': 'foo', 'fake': 'bar', 'default': 'default'}, ['fake', 'lang'])) + + # Last resort + self.assertEqual('foo', i18n.decode({'1': 'foo', '2': 'bar'}, 'fake')) + + # Primed accept_language + self.assertEqual('foo', i18n.decode({'1': 'foo', '2': 'bar', 'default': 'default'}, '1-a')) + + # Primed i18n value + self.assertEqual('bar', i18n.decode({'1-a': 'foo', '1': 'bar', 'default': 'default'}, '1-b')) + self.assertEqual('foo', i18n.decode({'1-a': 'foo', '2': 'bar', 'default': 'default'}, '1-b')) + + def test_decode_EnAsTheLastResort(self): + i18n._default_langs = ['en-us'] + self.assertEqual('right', i18n.decode({'a': 'wrong', 'en': 'right'}, 'probe')) + self.assertEqual('exact', i18n.decode({'a': 'wrong', 'en': 'right', 'probe': 'exact'}, 'probe')) + + def test_encode(self): + self.assertEqual({ + 'en': 'Delete Log File', + 'es': 'Borrar el archivo de registro', + 'fr': 'Supprimer le fichier log', + }, i18n.encode('Delete Log File')) + + self.assertEqual({ + 'en': "Error: Can't open file 'probe'\n", + 'es': "Error: No se puede abrir el archivo 'probe'\n", + 'fr': "Erreur : Ouverture du fichier 'probe' impossible\n", + }, i18n.encode("Error: Can't open file '%s'\n", 'probe')) + + self.assertEqual({ + 'en': "Error: Can't open file '1'\n", + 'es': "Error: No se puede abrir el archivo '2'\n", + 'fr': "Erreur : Ouverture du fichier '3' impossible\n", + }, i18n.encode("Error: Can't open file '%s'\n", {'en': 1, 'es': 2, 'fr': 3})) + + self.assertEqual({ + 'en': '1 when deleting 5', + 'es': '2 borrando 6', + 'fr': '3 lors de la suppression de 7', + }, i18n.encode('%(error)s when deleting %(file)s', error={'en': 1, 'es': 2, 'fr': 3}, file={'en': 5, 'es': 6, 'fr': 7})) + + +if __name__ == '__main__': + tests.main() diff --git a/tests/units/toolkit/router.py b/tests/units/toolkit/router.py index a9b17f2..3dd1306 100755 --- a/tests/units/toolkit/router.py +++ b/tests/units/toolkit/router.py @@ -9,9 +9,10 @@ from cStringIO import StringIO from __init__ import tests, src_root -from sugar_network import db, client -from sugar_network.toolkit.router import Blob, Router, Request, _parse_accept_language, route, fallbackroute, preroute, postroute, _filename -from sugar_network.toolkit import default_lang, http, coroutine +from sugar_network import db, client, toolkit +from sugar_network.toolkit.router import Router, Request, _parse_accept_language, route, fallbackroute, preroute, postroute +from sugar_network.toolkit.coroutine import this +from sugar_network.toolkit import http, coroutine class RouterTest(tests.Test): @@ -499,26 +500,44 @@ class RouterTest(tests.Test): def test_routes_Pre(self): - class Routes(object): + class A(object): @route('PROBE') def ok(self, request, response): return request['probe'] @preroute - def preroute(self, op, request, response): - request['probe'] = 'request' + def _(self, op, request, response): + request['probe'] = '_' - router = Router(Routes()) + class B1(A): + + @preroute + def z(self, op, request, response): + request['probe'] += 'z' + + class B2(object): + + @preroute + def f(self, op, request, response): + request['probe'] += 'f' + + class C(B1, B2): + + @preroute + def a(self, op, request, response): + request['probe'] += 'a' + + router = Router(C()) self.assertEqual( - ['request'], + ['_afz'], [i for i in router({'REQUEST_METHOD': 'PROBE', 'PATH_INFO': '/'}, lambda *args: None)]) def test_routes_Post(self): postroutes = [] - class Routes(object): + class A(object): @route('OK') def ok(self): @@ -529,20 +548,51 @@ class RouterTest(tests.Test): raise Exception('fail') @postroute - def postroute(self, request, response, result, exception): - postroutes.append((result, str(exception))) + def _(self, request, response, result, exception): + postroutes.append(('_', result, str(exception))) - router = Router(Routes()) + class B1(A): + + @postroute + def z(self, request, response, result, exception): + postroutes.append(('z', result, str(exception))) + + class B2(object): + + @postroute + def f(self, request, response, result, exception): + postroutes.append(('f', result, str(exception))) + + class C(B1, B2): + + @postroute + def a(self, request, response, result, exception): + postroutes.append(('a', result, str(exception))) + + router = Router(C()) self.assertEqual( ['ok'], [i for i in router({'REQUEST_METHOD': 'OK', 'PATH_INFO': '/'}, lambda *args: None)]) - self.assertEqual(('ok', 'None'), postroutes[-1]) + self.assertEqual([ + ('_', 'ok', 'None'), + ('a', 'ok', 'None'), + ('f', 'ok', 'None'), + ('z', 'ok', 'None'), + ], + postroutes) + del postroutes[:] self.assertEqual( ['{"request": "/", "error": "fail"}'], [i for i in router({'REQUEST_METHOD': 'FAIL', 'PATH_INFO': '/'}, lambda *args: None)]) - self.assertEqual((None, 'fail'), postroutes[-1]) + self.assertEqual([ + ('_', None, 'fail'), + ('a', None, 'fail'), + ('f', None, 'fail'), + ('z', None, 'fail'), + ], + postroutes) def test_routes_WildcardsAsLastResort(self): @@ -968,14 +1018,14 @@ class RouterTest(tests.Test): ], response) - def test_BlobsRedirects(self): + def test_FilesRedirects(self): URL = 'http://sugarlabs.org' class CommandsProcessor(object): @route('GET') def get(self, response): - return Blob(url=URL) + return toolkit.File(meta={'url': URL}) router = Router(CommandsProcessor()) @@ -1171,33 +1221,18 @@ class RouterTest(tests.Test): ], response) - def test_filename(self): - self.assertEqual('Foo', _filename('foo', None)) - self.assertEqual('Foo-Bar', _filename(['foo', 'bar'], None)) - self.assertEqual('FOO-BaR', _filename([' f o o', ' ba r '], None)) - - self.assertEqual('12-3', _filename(['/1/2/', '/3/'], None)) - - self.assertEqual('Foo.png', _filename('foo', 'image/png')) - self.assertEqual('Foo-Bar.gif', _filename(['foo', 'bar'], 'image/gif')) - self.assertEqual('Fake', _filename('fake', 'foo/bar')) - - self.assertEqual('Eng', _filename({default_lang(): 'eng'}, None)) - self.assertEqual('Eng', _filename([{default_lang(): 'eng'}], None)) - self.assertEqual('Bar-1', _filename([{'lang': 'foo', default_lang(): 'bar'}, '1'], None)) - - def test_BlobsDisposition(self): + def test_FilesDisposition(self): self.touch(('blob.data', 'value')) class CommandsProcessor(object): @route('GET', [], '1') def cmd1(self, request): - return Blob(name='foo', blob='blob.data') + return toolkit.File('blob.data', {'name': 'foo', 'mime_type': 'application/octet-stream'}) @route('GET', [], cmd='2') def cmd2(self, request): - return Blob(filename='foo.bar', blob='blob.data') + return toolkit.File('blob.data', {'filename': 'foo.bar'}) router = Router(CommandsProcessor()) @@ -1216,7 +1251,7 @@ class RouterTest(tests.Test): 'last-modified': formatdate(os.stat('blob.data').st_mtime, localtime=False, usegmt=True), 'content-length': str(len(result)), 'content-type': 'application/octet-stream', - 'content-disposition': 'attachment; filename="Foo.obj"', + 'content-disposition': 'attachment; filename="foo.obj"', } ], response) @@ -1292,7 +1327,6 @@ class RouterTest(tests.Test): [i for i in reply]) def test_SpawnEventStream(self): - events = [] class Routes(object): @@ -1301,8 +1335,10 @@ class RouterTest(tests.Test): yield {} yield {'foo': 'bar'} - def broadcast(self, event): - events.append(event.copy()) + events = [] + def localcast(event): + events.append(event.copy()) + this.localcast = localcast reply = Router(Routes(), allow_spawn=True)({ 'PATH_INFO': '/resource/guid/prop', @@ -1321,7 +1357,6 @@ class RouterTest(tests.Test): del events[:] def test_SpawnEventStreamFailure(self): - events = [] class Routes(object): @@ -1332,8 +1367,10 @@ class RouterTest(tests.Test): yield {'foo': 'bar'}, {'add': 'on'} raise RuntimeError('error') - def broadcast(self, event): - events.append(event.copy()) + events = [] + def localcast(event): + events.append(event.copy()) + this.localcast = localcast reply = Router(Routes(), allow_spawn=True)({ 'PATH_INFO': '/', @@ -1353,7 +1390,6 @@ class RouterTest(tests.Test): del events[:] def test_ReadRequestOnEventStreamSpawn(self): - events = [] class Routes(object): @@ -1362,8 +1398,10 @@ class RouterTest(tests.Test): yield {} yield {'request': request.content} - def broadcast(self, event): - events.append(event.copy()) + events = [] + def localcast(event): + events.append(event.copy()) + this.localcast = localcast reply = Router(Routes(), allow_spawn=True)({ 'PATH_INFO': '/', diff --git a/tests/units/toolkit/toolkit.py b/tests/units/toolkit/toolkit.py index 8c13b84..07ed9c6 100755 --- a/tests/units/toolkit/toolkit.py +++ b/tests/units/toolkit/toolkit.py @@ -8,7 +8,7 @@ from cStringIO import StringIO from __init__ import tests from sugar_network import toolkit -from sugar_network.toolkit import Seqno, Sequence +from sugar_network.toolkit import Seqno, Sequence, File class UtilTest(tests.Test): @@ -421,33 +421,14 @@ class UtilTest(tests.Test): ['d', 'a', 'b', 'c'], [i for i in stack]) - def test_gettext(self): - # Fallback to default lang - toolkit._default_langs = ['default'] - self.assertEqual('foo', toolkit.gettext({'lang': 'foo', 'default': 'bar'}, 'lang')) - self.assertEqual('bar', toolkit.gettext({'lang': 'foo', 'default': 'bar'}, 'fake')) - - # Exact accept_language - self.assertEqual('', toolkit.gettext(None, 'lang')) - self.assertEqual('foo', toolkit.gettext('foo', 'lang')) - self.assertEqual('foo', toolkit.gettext({'lang': 'foo', 'fake': 'bar', 'default': 'default'}, 'lang')) - self.assertEqual('foo', toolkit.gettext({'lang': 'foo', 'fake': 'bar', 'default': 'default'}, ['lang', 'fake'])) - self.assertEqual('bar', toolkit.gettext({'lang': 'foo', 'fake': 'bar', 'default': 'default'}, ['fake', 'lang'])) - - # Last resort - self.assertEqual('foo', toolkit.gettext({'1': 'foo', '2': 'bar'}, 'fake')) - - # Primed accept_language - self.assertEqual('foo', toolkit.gettext({'1': 'foo', '2': 'bar', 'default': 'default'}, '1-a')) - - # Primed i18n value - self.assertEqual('bar', toolkit.gettext({'1-a': 'foo', '1': 'bar', 'default': 'default'}, '1-b')) - self.assertEqual('foo', toolkit.gettext({'1-a': 'foo', '2': 'bar', 'default': 'default'}, '1-b')) - - def test_gettext_EnAsTheLastResort(self): - toolkit._default_langs = ['en-us'] - self.assertEqual('right', toolkit.gettext({'a': 'wrong', 'en': 'right'}, 'probe')) - self.assertEqual('exact', toolkit.gettext({'a': 'wrong', 'en': 'right', 'probe': 'exact'}, 'probe')) + def test_FileName(self): + self.assertEqual('blob', File().name) + self.assertEqual('blob', File('foo/bar').name) + self.assertEqual('digest', File(digest='digest').name) + self.assertEqual('foo', File(meta={'filename': 'foo'}).name) + self.assertEqual('foo', File(meta={'name': 'foo'}).name) + self.assertEqual('foo', File(meta={'filename': 'foo', 'mime_type': 'image/png'}).name) + self.assertEqual('digest.png', File(digest='digest', meta={'mime_type': 'image/png'}).name) if __name__ == '__main__': |