diff options
Diffstat (limited to 'tests/units')
43 files changed, 3827 insertions, 3796 deletions
diff --git a/tests/units/__main__.py b/tests/units/__main__.py index 22664cb..1df3e3e 100644 --- a/tests/units/__main__.py +++ b/tests/units/__main__.py @@ -5,7 +5,7 @@ from __init__ import tests from toolkit.__main__ import * from db.__main__ import * from node.__main__ import * -from resources.__main__ import * +from model.__main__ import * from client.__main__ import * if __name__ == '__main__': diff --git a/tests/units/client/__main__.py b/tests/units/client/__main__.py index fd37288..fc1d045 100644 --- a/tests/units/client/__main__.py +++ b/tests/units/client/__main__.py @@ -3,12 +3,12 @@ from __init__ import tests from clones import * -from commands import * +from routes import * from injector import * from journal import * -from offline_commands import * -from online_commands import * -from server_commands import * +from offline_routes import * +from online_routes import * +from server_routes import * from solver import * from cache import * diff --git a/tests/units/client/clones.py b/tests/units/client/clones.py index b9178a1..974adca 100755 --- a/tests/units/client/clones.py +++ b/tests/units/client/clones.py @@ -8,18 +8,18 @@ from os.path import abspath, lexists, exists from __init__ import tests -from sugar_network.resources.user import User -from sugar_network.resources.context import Context +from sugar_network import db, model +from sugar_network.model.user import User +from sugar_network.model.context import Context from sugar_network.client import clones -from sugar_network.toolkit import coroutine, util -from sugar_network.resources.volume import Volume +from sugar_network.toolkit import coroutine class CloneTest(tests.Test): def setUp(self): tests.Test.setUp(self) - self.volume = Volume('local', [User, Context]) + self.volume = db.Volume('local', [User, Context]) self.job = None def tearDown(self): @@ -371,7 +371,7 @@ class CloneTest(tests.Test): assert not lexists('share/mime/application/x-foo-bar.xml') def test_Sync(self): - volume = Volume('client') + volume = db.Volume('client', model.RESOURCES) volume['context'].create({ 'guid': 'context1', 'type': 'activity', @@ -409,7 +409,7 @@ class CloneTest(tests.Test): self.assertEqual(0, volume['context'].get('context3')['clone']) def test_SyncByMtime(self): - volume = Volume('client') + volume = db.Volume('client', model.RESOURCES) volume['context'].create({ 'guid': 'context', 'type': 'activity', diff --git a/tests/units/client/injector.py b/tests/units/client/injector.py index 256cfa4..c198f81 100755 --- a/tests/units/client/injector.py +++ b/tests/units/client/injector.py @@ -17,9 +17,9 @@ from __init__ import tests from sugar_network.client import journal from sugar_network.toolkit import coroutine, enforce, pipe as pipe_, lsb_release from sugar_network.node import obs -from sugar_network.resources.user import User -from sugar_network.resources.context import Context -from sugar_network.resources.implementation import Implementation +from sugar_network.model.user import User +from sugar_network.model.context import Context +from sugar_network.model.implementation import Implementation from sugar_network.client import IPCClient, packagekit, injector, clones, solver from sugar_network import client diff --git a/tests/units/client/journal.py b/tests/units/client/journal.py index ffb6e4f..fd36a2a 100755 --- a/tests/units/client/journal.py +++ b/tests/units/client/journal.py @@ -12,6 +12,7 @@ from __init__ import tests from sugar_network import db from sugar_network.client import journal, ipc_port +from sugar_network.toolkit.router import Request, Response class JournalTest(tests.Test): @@ -76,7 +77,7 @@ class JournalTest(tests.Test): self.assertEqual('data', file(self.ds.get_filename(guid)).read()) def test_Update(self): - ds = journal.Commands() + ds = journal.Routes() self.touch(('preview', 'preview1')) ds.journal_update('guid', StringIO('data1'), title='title1', description='description1', preview={'blob': 'preview'}) @@ -97,76 +98,73 @@ class JournalTest(tests.Test): def test_FindRequest(self): url = 'http://127.0.0.1:%s/journal/' % ipc_port.value - ds = journal.Commands() + ds = journal.Routes() ds.journal_update('guid1', StringIO('data1'), title='title1', description='description1', preview=StringIO('preview1')) ds.journal_update('guid2', StringIO('data2'), title='title2', description='description2', preview=StringIO('preview2')) ds.journal_update('guid3', StringIO('data3'), title='title3', description='description3', preview=StringIO('preview3')) - request = db.Request() + request = Request(reply=['uid', 'title', 'description', 'preview']) request.path = ['journal'] - response = db.Response() + response = Response() self.assertEqual([ {'guid': 'guid1', 'title': 'title1', 'description': 'description1', 'preview': url + 'guid1/preview'}, {'guid': 'guid2', 'title': 'title2', 'description': 'description2', 'preview': url + 'guid2/preview'}, {'guid': 'guid3', 'title': 'title3', 'description': 'description3', 'preview': url + 'guid3/preview'}, ], - ds.journal(request, response)['result']) - self.assertEqual('application/json', response.content_type) + ds.journal_find(request, response)['result']) - request = db.Request(offset=1, limit=1) + request = Request(offset=1, limit=1, reply=['uid', 'title', 'description', 'preview']) request.path = ['journal'] self.assertEqual([ {'guid': 'guid2', 'title': 'title2', 'description': 'description2', 'preview': url + 'guid2/preview'}, ], - ds.journal(request, response)['result']) + ds.journal_find(request, response)['result']) - request = db.Request(query='title3') + request = Request(query='title3', reply=['uid', 'title', 'description', 'preview']) request.path = ['journal'] self.assertEqual([ {'guid': 'guid3', 'title': 'title3', 'description': 'description3', 'preview': url + 'guid3/preview'}, ], - ds.journal(request, response)['result']) + ds.journal_find(request, response)['result']) - request = db.Request(order_by='+title') + request = Request(order_by=['+title'], reply=['uid', 'title', 'description', 'preview']) request.path = ['journal'] self.assertEqual([ {'guid': 'guid3', 'title': 'title3', 'description': 'description3', 'preview': url + 'guid3/preview'}, {'guid': 'guid2', 'title': 'title2', 'description': 'description2', 'preview': url + 'guid2/preview'}, {'guid': 'guid1', 'title': 'title1', 'description': 'description1', 'preview': url + 'guid1/preview'}, ], - ds.journal(request, response)['result']) + ds.journal_find(request, response)['result']) def test_GetRequest(self): url = 'http://127.0.0.1:%s/journal/' % ipc_port.value - ds = journal.Commands() + ds = journal.Routes() ds.journal_update('guid1', StringIO('data1'), title='title1', description='description1', preview=StringIO('preview1')) - request = db.Request() + request = Request() request.path = ['journal', 'guid1'] - response = db.Response() + response = Response() self.assertEqual( {'guid': 'guid1', 'title': 'title1', 'description': 'description1', 'preview': url + 'guid1/preview'}, - ds.journal(request, response)) - self.assertEqual('application/json', response.content_type) + ds.journal_get(request, response)) def test_GetPropRequest(self): - ds = journal.Commands() + ds = journal.Routes() ds.journal_update('guid1', StringIO('data1'), title='title1', description='description1', preview=StringIO('preview1')) - request = db.Request() + request = Request() request.path = ['journal', 'guid1', 'title'] - response = db.Response() - self.assertEqual('title1', ds.journal(request, response)) - self.assertEqual('application/json', response.content_type) + response = Response() + self.assertEqual('title1', ds.journal_get_prop(request, response)) - request = db.Request() + request = Request() request.path = ['journal', 'guid1', 'preview'] - response = db.Response() + response = Response() self.assertEqual({ 'mime_type': 'image/png', 'blob': '.sugar/default/datastore/gu/guid1/metadata/preview', - }, ds.journal(request, response)) + }, ds.journal_get_preview(request, response)) self.assertEqual(None, response.content_type) diff --git a/tests/units/client/offline_commands.py b/tests/units/client/offline_routes.py index 69d5f2a..ced30d1 100755 --- a/tests/units/client/offline_commands.py +++ b/tests/units/client/offline_routes.py @@ -5,21 +5,21 @@ from os.path import exists from __init__ import tests, src_root -from sugar_network import client +from sugar_network import client, model from sugar_network.client import IPCClient, clones -from sugar_network.client.commands import ClientCommands -from sugar_network.client import IPCRouter -from sugar_network.resources.volume import Volume +from sugar_network.client.routes import ClientRoutes +from sugar_network.db import Volume +from sugar_network.toolkit.router import Router from sugar_network.toolkit import coroutine, http -class OfflineCommandsTest(tests.Test): +class OfflineRoutes(tests.Test): def setUp(self): tests.Test.setUp(self) - self.home_volume = Volume('db') - commands = ClientCommands(self.home_volume) - server = coroutine.WSGIServer(('127.0.0.1', client.ipc_port.value), IPCRouter(commands)) + self.home_volume = Volume('db', model.RESOURCES) + commands = ClientRoutes(self.home_volume) + server = coroutine.WSGIServer(('127.0.0.1', client.ipc_port.value), Router(commands)) coroutine.spawn(server.serve_forever) coroutine.dispatch() @@ -127,9 +127,9 @@ class OfflineCommandsTest(tests.Test): job.kill() self.assertEqual([ - {'guid': guid, 'document': 'context', 'event': 'create'}, - {'guid': guid, 'document': 'context', 'event': 'update'}, - {'guid': guid, 'event': 'delete', 'document': 'context'}, + {'guid': guid, 'resource': 'context', 'event': 'create'}, + {'guid': guid, 'resource': 'context', 'event': 'update'}, + {'guid': guid, 'event': 'delete', 'resource': 'context'}, ], events) diff --git a/tests/units/client/online_commands.py b/tests/units/client/online_routes.py index 692fa38..10d9b4b 100755 --- a/tests/units/client/online_commands.py +++ b/tests/units/client/online_routes.py @@ -11,24 +11,26 @@ from os.path import exists from __init__ import tests, src_root -from sugar_network import client, db -from sugar_network.client import IPCClient, journal, clones, injector, commands +from sugar_network import client, db, model +from sugar_network.client import IPCClient, journal, clones, injector, routes from sugar_network.toolkit import coroutine, http from sugar_network.toolkit.spec import Spec -from sugar_network.client.commands import ClientCommands -from sugar_network.resources.volume import Volume, Resource -from sugar_network.resources.user import User -from sugar_network.resources.context import Context -from sugar_network.resources.implementation import Implementation -from sugar_network.resources.artifact import Artifact +from sugar_network.client.routes import ClientRoutes, Request, Response +from sugar_network.node.master import MasterRoutes +from sugar_network.db import Volume, Resource +from sugar_network.model.user import User +from sugar_network.model.context import Context +from sugar_network.model.implementation import Implementation +from sugar_network.model.artifact import Artifact +from sugar_network.toolkit.router import route import requests -class OnlineCommandsTest(tests.Test): +class OnlineRoutes(tests.Test): def test_inline(self): - cp = ClientCommands(Volume('client'), client.api_url.value) + cp = ClientRoutes(Volume('client', model.RESOURCES), client.api_url.value) assert not cp.inline() trigger = self.wait_for_events(cp, event='inline', state='online') @@ -38,8 +40,8 @@ class OnlineCommandsTest(tests.Test): assert trigger.value is None assert not cp.inline() - request = db.Request(method='GET', cmd='whoami') - cp.call(request) + request = Request(method='GET', cmd='whoami') + cp.whoami(request, Response()) trigger.wait() assert cp.inline() @@ -290,9 +292,9 @@ class OnlineCommandsTest(tests.Test): kwargs['data'] = data.read() updates.append((guid, kwargs)) - self.override(journal.Commands, '__init__', lambda *args: None) - self.override(journal.Commands, 'journal_update', journal_update) - self.override(journal.Commands, 'journal_delete', lambda self, guid: updates.append((guid,))) + self.override(journal.Routes, '__init__', lambda *args: None) + self.override(journal.Routes, 'journal_update', journal_update) + self.override(journal.Routes, 'journal_delete', lambda self, guid: updates.append((guid,))) ipc = IPCClient() @@ -368,9 +370,9 @@ class OnlineCommandsTest(tests.Test): kwargs['data'] = data.read() updates.append((guid, kwargs)) - self.override(journal.Commands, '__init__', lambda *args: None) - self.override(journal.Commands, 'journal_update', journal_update) - self.override(journal.Commands, 'journal_delete', lambda self, guid: updates.append((guid,))) + self.override(journal.Routes, '__init__', lambda *args: None) + self.override(journal.Routes, 'journal_update', journal_update) + self.override(journal.Routes, 'journal_delete', lambda self, guid: updates.append((guid,))) ipc = IPCClient() @@ -497,9 +499,9 @@ class OnlineCommandsTest(tests.Test): job.kill() self.assertEqual([ - {'guid': guid, 'document': 'context', 'event': 'create'}, - {'guid': guid, 'document': 'context', 'event': 'update'}, - {'guid': guid, 'event': 'delete', 'document': 'context'}, + {'guid': guid, 'resource': 'context', 'event': 'create'}, + {'guid': guid, 'resource': 'context', 'event': 'update'}, + {'guid': guid, 'event': 'delete', 'resource': 'context'}, ], events) del events[:] @@ -522,9 +524,9 @@ class OnlineCommandsTest(tests.Test): job.kill() self.assertEqual([ - {'guid': guid, 'document': 'context', 'event': 'create'}, - {'guid': guid, 'document': 'context', 'event': 'update'}, - {'guid': guid, 'event': 'delete', 'document': 'context'}, + {'guid': guid, 'resource': 'context', 'event': 'create'}, + {'guid': guid, 'resource': 'context', 'event': 'update'}, + {'guid': guid, 'event': 'delete', 'resource': 'context'}, ], events) @@ -1097,7 +1099,7 @@ class OnlineCommandsTest(tests.Test): }}) - trigger = self.wait_for_events(ipc, event='update', document='context', guid=context1) + trigger = self.wait_for_events(ipc, event='update', resource='context', guid=context1) ipc.put(['context', context1], 2, cmd='clone') trigger.wait() self.assertEqual( @@ -1110,7 +1112,7 @@ class OnlineCommandsTest(tests.Test): 'summary': 'summary', 'description': 'description', }) - trigger = self.wait_for_events(ipc, event='create', document='context', guid=context2) + trigger = self.wait_for_events(ipc, event='create', resource='context', guid=context2) ipc.put(['context', context2], True, cmd='favorite') trigger.wait() self.assertEqual( @@ -1118,59 +1120,63 @@ class OnlineCommandsTest(tests.Test): ipc.get(['context', context2], reply=['favorite'])) def test_FallbackToLocalSNOnRemoteTransportFails(self): - local_pid = os.getpid() - class Document(Resource): + class LocalRoutes(routes._LocalRoutes): - @db.document_command(method='GET', cmd='sleep') + @route('GET', cmd='sleep') def sleep(self): - if os.getpid() == local_pid: - return 'local' - else: - coroutine.sleep(.5) - return 'remote' + return 'local' - @db.document_command(method='GET', cmd='yield_raw_and_sleep', + @route('GET', cmd='yield_raw_and_sleep', mime_type='application/octet-stream') def yield_raw_and_sleep(self): - if os.getpid() == local_pid: - yield 'local' - else: - for __ in range(33): - yield "remote\n" - coroutine.sleep(.5) - for __ in range(33): - yield "remote\n" - - @db.document_command(method='GET', cmd='yield_json_and_sleep', + yield 'local' + + @route('GET', cmd='yield_json_and_sleep', mime_type='application/json') def yield_json_and_sleep(self): - if os.getpid() == local_pid: - yield '"local"' - else: - yield '"' - yield 'r' - coroutine.sleep(.5) - yield 'emote"' - - home_volume = self.start_client([User, Document]) + yield '"local"' + + self.override(routes, '_LocalRoutes', LocalRoutes) + home_volume = self.start_client([User]) ipc = IPCClient() - guid = ipc.post(['document'], {}) - self.assertEqual('local', ipc.get(['document', guid], cmd='sleep')) - self.assertEqual('local', ipc.get(['document', guid], cmd='yield_raw_and_sleep')) - self.assertEqual('local', ipc.get(['document', guid], cmd='yield_json_and_sleep')) + self.assertEqual('local', ipc.get(cmd='sleep')) + self.assertEqual('local', ipc.get(cmd='yield_raw_and_sleep')) + self.assertEqual('local', ipc.get(cmd='yield_json_and_sleep')) + + class NodeRoutes(MasterRoutes): + + @route('GET', cmd='sleep') + def sleep(self): + coroutine.sleep(.5) + return 'remote' + + @route('GET', cmd='yield_raw_and_sleep', + mime_type='application/octet-stream') + def yield_raw_and_sleep(self): + for __ in range(33): + yield "remote\n" + coroutine.sleep(.5) + for __ in range(33): + yield "remote\n" - node_pid = self.fork_master([User, Document]) + @route('GET', cmd='yield_json_and_sleep', + mime_type='application/json') + def yield_json_and_sleep(self): + yield '"' + yield 'r' + coroutine.sleep(.5) + yield 'emote"' + + node_pid = self.fork_master([User], NodeRoutes) ipc.get(cmd='inline') self.wait_for_events(ipc, event='inline', state='online').wait() - guid = ipc.post(['document'], {}) - home_volume['document'].create({'guid': guid}) ts = time.time() - self.assertEqual('remote', ipc.get(['document', guid], cmd='sleep')) - self.assertEqual('remote\n' * 66, ipc.get(['document', guid], cmd='yield_raw_and_sleep')) - self.assertEqual('remote', ipc.get(['document', guid], cmd='yield_json_and_sleep')) + self.assertEqual('remote', ipc.get(cmd='sleep')) + self.assertEqual('remote\n' * 66, ipc.get(cmd='yield_raw_and_sleep')) + self.assertEqual('remote', ipc.get(cmd='yield_json_and_sleep')) assert time.time() - ts >= 1.5 def kill(): @@ -1178,27 +1184,27 @@ class OnlineCommandsTest(tests.Test): self.waitpid(node_pid) coroutine.spawn(kill) - self.assertEqual('local', ipc.get(['document', guid], cmd='sleep')) + self.assertEqual('local', ipc.get(cmd='sleep')) assert not ipc.get(cmd='inline') - node_pid = self.fork_master([User, Document]) + node_pid = self.fork_master([User], NodeRoutes) ipc.get(cmd='inline') self.wait_for_events(ipc, event='inline', state='online').wait() coroutine.spawn(kill) - self.assertEqual('local', ipc.get(['document', guid], cmd='yield_raw_and_sleep')) + self.assertEqual('local', ipc.get(cmd='yield_raw_and_sleep')) assert not ipc.get(cmd='inline') - node_pid = self.fork_master([User, Document]) + node_pid = self.fork_master([User], NodeRoutes) ipc.get(cmd='inline') self.wait_for_events(ipc, event='inline', state='online').wait() coroutine.spawn(kill) - self.assertEqual('local', ipc.get(['document', guid], cmd='yield_json_and_sleep')) + self.assertEqual('local', ipc.get(cmd='yield_json_and_sleep')) assert not ipc.get(cmd='inline') def test_ReconnectOnServerFall(self): - commands._RECONNECT_TIMEOUT = 1 + routes._RECONNECT_TIMEOUT = 1 node_pid = self.fork_master([User]) self.start_client([User]) diff --git a/tests/units/client/commands.py b/tests/units/client/routes.py index 691eeea..8ae8d44 100755 --- a/tests/units/client/commands.py +++ b/tests/units/client/routes.py @@ -5,25 +5,24 @@ import json from __init__ import tests -from sugar_network import db, client +from sugar_network import db, client, model from sugar_network.client import journal, injector, IPCClient -from sugar_network.client.commands import ClientCommands, CachedClientCommands -from sugar_network.resources.volume import Volume -from sugar_network.resources.user import User -from sugar_network.resources.report import Report -from sugar_network.client import IPCRouter +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 import requests -class CommandsTest(tests.Test): +class RoutesTest(tests.Test): def test_Hub(self): - volume = Volume('db') - cp = ClientCommands(volume) + volume = db.Volume('db', model.RESOURCES) + cp = ClientRoutes(volume) server = coroutine.WSGIServer( - ('127.0.0.1', client.ipc_port.value), IPCRouter(cp)) + ('127.0.0.1', client.ipc_port.value), Router(cp)) coroutine.spawn(server.serve_forever) coroutine.dispatch() @@ -45,13 +44,11 @@ class CommandsTest(tests.Test): def test_launch(self): self.override(injector, 'launch', lambda *args, **kwargs: [{'args': args, 'kwargs': kwargs}]) - volume = Volume('db') - cp = ClientCommands(volume) - - self.assertRaises(RuntimeError, cp.launch, 'fake-document', 'app', []) + volume = db.Volume('db', model.RESOURCES) + cp = ClientRoutes(volume) trigger = self.wait_for_events(cp, event='launch') - cp.launch('context', 'app', []) + cp.launch(Request(path=['context', 'app']), []) self.assertEqual( {'event': 'launch', 'args': ['app', []], 'kwargs': {'color': None, 'activity_id': None, 'uri': None, 'object_id': None}}, trigger.wait()) @@ -59,11 +56,11 @@ class CommandsTest(tests.Test): def test_launch_ResumeJobject(self): self.override(injector, 'launch', lambda *args, **kwargs: [{'args': args, 'kwargs': kwargs}]) self.override(journal, 'exists', lambda *args: True) - volume = Volume('db') - cp = ClientCommands(volume) + volume = db.Volume('db', model.RESOURCES) + cp = ClientRoutes(volume) trigger = self.wait_for_events(cp, event='launch') - cp.launch('context', 'app', [], object_id='object_id') + cp.launch(Request(path=['context', 'app']), [], object_id='object_id') self.assertEqual( {'event': 'launch', 'args': ['app', []], 'kwargs': {'color': None, 'activity_id': None, 'uri': None, 'object_id': 'object_id'}}, trigger.wait()) @@ -165,9 +162,9 @@ class CommandsTest(tests.Test): ipc.get(['context'], reply=['guid', 'title'], favorite=False)['result']) def test_SetLocalLayerInOffline(self): - volume = Volume('client') - cp = ClientCommands(volume, client.api_url.value) - post = db.Request(method='POST', document='context') + volume = db.Volume('client', model.RESOURCES) + cp = ClientRoutes(volume, client.api_url.value) + post = Request(method='POST', path=['context']) post.content_type = 'application/json' post.content = { 'type': 'activity', @@ -176,22 +173,22 @@ class CommandsTest(tests.Test): 'description': 'description', } - guid = cp.call(post) - self.assertEqual(['public', 'local'], cp.call(db.Request(method='GET', document='context', guid=guid, prop='layer'))) + guid = call(cp, post) + self.assertEqual(['public', 'local'], call(cp, Request(method='GET', path=['context', guid, 'layer']))) trigger = self.wait_for_events(cp, event='inline', state='online') node_volume = self.start_master() - cp.call(db.Request(method='GET', cmd='inline')) + call(cp, Request(method='GET', cmd='inline')) trigger.wait() - guid = cp.call(post) - self.assertEqual(['public'], cp.call(db.Request(method='GET', document='context', guid=guid, prop='layer'))) + guid = call(cp, post) + self.assertEqual(['public'], call(cp, Request(method='GET', path=['context', guid, 'layer']))) def test_CachedClientCommands(self): - volume = Volume('client') - cp = CachedClientCommands(volume, client.api_url.value) + volume = db.Volume('client', model.RESOURCES) + cp = CachedClientRoutes(volume, client.api_url.value) - post = db.Request(method='POST', document='context') + post = Request(method='POST', path=['context']) post.content_type = 'application/json' post.content = { 'type': 'activity', @@ -199,12 +196,12 @@ class CommandsTest(tests.Test): 'summary': 'summary', 'description': 'description', } - guid1 = cp.call(post) - guid2 = cp.call(post) + guid1 = call(cp, post) + guid2 = call(cp, post) trigger = self.wait_for_events(cp, event='push') self.start_master() - cp.call(db.Request(method='GET', cmd='inline')) + call(cp, Request(method='GET', cmd='inline')) trigger.wait() self.assertEqual([[3, None]], json.load(file('client/push.sequence'))) @@ -229,7 +226,7 @@ class CommandsTest(tests.Test): trigger = self.wait_for_events(cp, event='push') self.start_master() - cp.call(db.Request(method='GET', cmd='inline')) + call(cp, Request(method='GET', cmd='inline')) trigger.wait() self.assertEqual([[4, None]], json.load(file('client/push.sequence'))) @@ -245,30 +242,30 @@ class CommandsTest(tests.Test): self.node_volume['context'].get(guid2)['author']) def test_CachedClientCommands_WipeReports(self): - volume = Volume('client') - cp = CachedClientCommands(volume, client.api_url.value) + volume = db.Volume('client', model.RESOURCES) + cp = CachedClientRoutes(volume, client.api_url.value) - post = db.Request(method='POST', document='report') + post = Request(method='POST', path=['report']) post.content_type = 'application/json' post.content = { 'context': 'context', 'error': 'error', } - guid = cp.call(post) + guid = call(cp, post) trigger = self.wait_for_events(cp, event='push') self.start_master([User, Report]) - cp.call(db.Request(method='GET', cmd='inline')) + call(cp, Request(method='GET', cmd='inline')) trigger.wait() assert not volume['report'].exists(guid) assert self.node_volume['report'].exists(guid) def test_SwitchToOfflineForAbsentOnlineProps(self): - volume = Volume('client') - cp = ClientCommands(volume, client.api_url.value) + volume = db.Volume('client', model.RESOURCES) + cp = ClientRoutes(volume, client.api_url.value) - post = db.Request(method='POST', document='context') + post = Request(method='POST', path=['context']) post.content_type = 'application/json' post.content = { 'type': 'activity', @@ -276,17 +273,22 @@ class CommandsTest(tests.Test): 'summary': 'summary', 'description': 'description', } - guid = cp.call(post) + guid = call(cp, post) - self.assertEqual('title', cp.call(db.Request(method='GET', document='context', guid=guid, prop='title'))) + self.assertEqual('title', call(cp, Request(method='GET', path=['context', guid, 'title']))) trigger = self.wait_for_events(cp, event='inline', state='online') self.start_master() - cp.call(db.Request(method='GET', cmd='inline')) + call(cp, Request(method='GET', cmd='inline')) trigger.wait() assert not self.node_volume['context'].exists(guid) - self.assertEqual('title', cp.call(db.Request(method='GET', document='context', guid=guid, prop='title'))) + self.assertEqual('title', call(cp, Request(method='GET', path=['context', guid, 'title']))) + + +def call(routes, request): + router = Router(routes) + return router.call(request, Response()) if __name__ == '__main__': diff --git a/tests/units/client/server_commands.py b/tests/units/client/server_routes.py index 771d008..bea8f7f 100755 --- a/tests/units/client/server_commands.py +++ b/tests/units/client/server_routes.py @@ -7,11 +7,11 @@ from os.path import exists from __init__ import tests, src_root -from sugar_network import db, client +from sugar_network import db, client, model from sugar_network.client import IPCClient -from sugar_network.client.commands import ClientCommands -from sugar_network.client import IPCRouter -from sugar_network.resources.volume import Volume +from sugar_network.client.routes import ClientRoutes +from sugar_network.db import Volume +from sugar_network.toolkit.router import Router from sugar_network.toolkit import mountpoints, coroutine @@ -19,20 +19,20 @@ class ServerCommandsTest(tests.Test): def start_node(self): os.makedirs('disk/sugar-network') - self.node_volume = Volume('db') - cp = ClientCommands(self.node_volume) + self.node_volume = Volume('db', model.RESOURCES) + cp = ClientRoutes(self.node_volume) trigger = self.wait_for_events(cp, event='inline', state='online') coroutine.spawn(mountpoints.monitor, tests.tmpdir) trigger.wait() - server = coroutine.WSGIServer(('127.0.0.1', client.ipc_port.value), IPCRouter(cp)) + server = coroutine.WSGIServer(('127.0.0.1', client.ipc_port.value), Router(cp)) coroutine.spawn(server.serve_forever) coroutine.dispatch() return cp def test_PopulateNode(self): os.makedirs('disk/sugar-network') - volume = Volume('db') - cp = ClientCommands(volume) + volume = Volume('db', model.RESOURCES) + cp = ClientRoutes(volume) assert not cp.inline() trigger = self.wait_for_events(cp, event='inline', state='online') @@ -41,8 +41,8 @@ class ServerCommandsTest(tests.Test): assert cp.inline() def test_MountNode(self): - volume = Volume('db') - cp = ClientCommands(volume) + volume = Volume('db', model.RESOURCES) + cp = ClientRoutes(volume) trigger = self.wait_for_events(cp, event='inline', state='online') mountpoints.populate('.') @@ -97,8 +97,8 @@ class ServerCommandsTest(tests.Test): job.kill() self.assertEqual([ - {'guid': guid, 'document': 'context', 'event': 'create'}, - {'guid': guid, 'document': 'context', 'event': 'update'}, + {'guid': guid, 'resource': 'context', 'event': 'create'}, + {'guid': guid, 'resource': 'context', 'event': 'update'}, ], events) diff --git a/tests/units/db/__main__.py b/tests/units/db/__main__.py index 70e829a..6dda7ff 100644 --- a/tests/units/db/__main__.py +++ b/tests/units/db/__main__.py @@ -2,15 +2,11 @@ from __init__ import tests -from commands import * -from document import * -from env import * +from resource import * from index import * -from metadata import * from migrate import * -from router import * from storage import * -from volume import * +from routes import * if __name__ == '__main__': tests.main() diff --git a/tests/units/db/commands.py b/tests/units/db/commands.py deleted file mode 100755 index da859d5..0000000 --- a/tests/units/db/commands.py +++ /dev/null @@ -1,552 +0,0 @@ -#!/usr/bin/env python -# sugar-lint: disable - -from cStringIO import StringIO - -from __init__ import tests - -from sugar_network import db -from sugar_network.db import env, volume, Volume, Document, \ - property_command, document_command, directory_command, volume_command, \ - Request, BlobProperty, Response, CommandsProcessor, \ - CommandNotFound, to_int, to_list -from sugar_network.db.router import route -from sugar_network.toolkit.http import NotFound, Forbidden - - -class CommandsTest(tests.Test): - - def test_VolumeCommands(self): - calls = [] - - class TestCommandsProcessor(CommandsProcessor): - - @volume_command(method='PROBE') - def command_1(self, **kwargs): - calls.append(('command_1', kwargs)) - - @volume_command(method='PROBE', cmd='command_2') - def command_2(self, **kwargs): - calls.append(('command_2', kwargs)) - - cp = TestCommandsProcessor() - - self.call(cp, 'PROBE') - self.assertRaises(CommandNotFound, self.call, cp, 'PROBE', cmd='command_1') - self.call(cp, 'PROBE', cmd='command_2') - - self.assertEqual([ - ('command_1', {}), - ('command_2', {}), - ], - calls) - - def test_DirectoryCommands(self): - calls = [] - - class TestCommandsProcessor(CommandsProcessor): - - @directory_command(method='PROBE') - def command_1(self, **kwargs): - calls.append(('command_1', kwargs)) - - @directory_command(method='PROBE', cmd='command_2') - def command_2(self, **kwargs): - calls.append(('command_2', kwargs)) - - cp = TestCommandsProcessor() - - self.assertRaises(CommandNotFound, self.call, cp, 'PROBE') - self.call(cp, 'PROBE', document='testdocument') - self.call(cp, 'PROBE', document='fakedocument') - self.assertRaises(CommandNotFound, self.call, cp, 'PROBE', cmd='command_1', document='testdocument') - self.call(cp, 'PROBE', cmd='command_2', document='testdocument') - self.call(cp, 'PROBE', cmd='command_2', document='fakedocument') - - self.assertEqual([ - ('command_1', {}), - ('command_1', {}), - ('command_2', {}), - ('command_2', {}), - ], - calls) - - def test_DocumentCommands(self): - calls = [] - - class TestCommandsProcessor(CommandsProcessor): - - @document_command(method='PROBE') - def command_1(self, **kwargs): - calls.append(('command_1', kwargs)) - - @document_command(method='PROBE', cmd='command_2') - def command_2(self, **kwargs): - calls.append(('command_2', kwargs)) - - class TestDocument(Document): - pass - - volume = Volume(tests.tmpdir, [TestDocument]) - cp = TestCommandsProcessor(volume) - - self.assertRaises(CommandNotFound, self.call, cp, 'PROBE') - self.assertRaises(CommandNotFound, self.call, cp, 'PROBE', document='testdocument') - self.call(cp, 'PROBE', document='testdocument', guid='guid') - self.call(cp, 'PROBE', document='fakedocument', guid='guid') - self.assertRaises(CommandNotFound, self.call, cp, 'PROBE', cmd='command_1', document='testdocument', guid='guid') - self.call(cp, 'PROBE', cmd='command_2', document='testdocument', guid='guid') - self.call(cp, 'PROBE', cmd='command_2', document='fakedocument', guid='guid') - - self.assertEqual([ - ('command_1', {}), - ('command_1', {}), - ('command_2', {}), - ('command_2', {}), - ], - calls) - - def test_PropertyCommands(self): - calls = [] - - class TestCommandsProcessor(CommandsProcessor): - - @property_command(method='PROBE') - def command_1(self, **kwargs): - calls.append(('command_1', kwargs)) - - @property_command(method='PROBE', cmd='command_2') - def command_2(self, **kwargs): - calls.append(('command_2', kwargs)) - - class TestDocument(Document): - pass - - volume = Volume(tests.tmpdir, [TestDocument]) - cp = TestCommandsProcessor(volume) - - self.assertRaises(CommandNotFound, self.call, cp, 'PROBE') - self.assertRaises(CommandNotFound, self.call, cp, 'PROBE', document='testdocument') - self.assertRaises(CommandNotFound, self.call, cp, 'PROBE', document='testdocument', guid='guid') - self.call(cp, 'PROBE', document='testdocument', guid='guid', prop='prop') - self.call(cp, 'PROBE', document='fakedocument', guid='guid', prop='prop') - self.assertRaises(CommandNotFound, self.call, cp, 'PROBE', cmd='command_1', document='testdocument', guid='guid', prop='prop') - self.call(cp, 'PROBE', cmd='command_2', document='testdocument', guid='guid', prop='prop') - self.call(cp, 'PROBE', cmd='command_2', document='fakedocument', guid='guid', prop='prop') - - self.assertEqual([ - ('command_1', {}), - ('command_1', {}), - ('command_2', {}), - ('command_2', {}), - ], - calls) - - def test_ClassDodcumentCommands(self): - calls = [] - - class TestDocument(Document): - - @document_command(method='PROBE') - def command_1(cls, **kwargs): - calls.append(('command_1', kwargs)) - - @document_command(method='PROBE', cmd='command_2') - def command_2(cls, **kwargs): - calls.append(('command_2', kwargs)) - - volume = Volume(tests.tmpdir, [TestDocument]) - cp = CommandsProcessor(volume) - - self.assertRaises(CommandNotFound, self.call, cp, 'PROBE') - self.assertRaises(CommandNotFound, self.call, cp, 'PROBE', document='testdocument') - self.assertRaises(NotFound, self.call, cp, 'PROBE', document='testdocument', guid='guid') - volume['testdocument'].create({'guid': 'guid'}) - self.call(cp, 'PROBE', document='testdocument', guid='guid') - self.assertRaises(CommandNotFound, self.call, cp, 'PROBE', document='fakedocument', guid='guid') - self.assertRaises(CommandNotFound, self.call, cp, 'PROBE', cmd='command_1', document='testdocument', guid='guid') - self.call(cp, 'PROBE', cmd='command_2', document='testdocument', guid='guid') - self.assertRaises(CommandNotFound, self.call, cp, 'PROBE', cmd='command_2', document='fakedocument', guid='guid') - - self.assertEqual([ - ('command_1', {}), - ('command_2', {}), - ], - calls) - - def test_ClassPropertyCommands(self): - calls = [] - - class TestDocument(Document): - - @property_command(method='PROBE') - def command_1(cls, **kwargs): - calls.append(('command_1', kwargs)) - - @property_command(method='PROBE', cmd='command_2') - def command_2(cls, **kwargs): - calls.append(('command_2', kwargs)) - - volume = Volume(tests.tmpdir, [TestDocument]) - cp = CommandsProcessor(volume) - - self.assertRaises(CommandNotFound, self.call, cp, 'PROBE') - self.assertRaises(CommandNotFound, self.call, cp, 'PROBE', document='testdocument') - self.assertRaises(CommandNotFound, self.call, cp, 'PROBE', document='testdocument', prop='prop') - self.assertRaises(NotFound, self.call, cp, 'PROBE', document='testdocument', guid='guid', prop='prop') - volume['testdocument'].create({'guid': 'guid'}) - self.call(cp, 'PROBE', document='testdocument', guid='guid', prop='prop') - self.assertRaises(CommandNotFound, self.call, cp, 'PROBE', document='fakedocument', guid='guid', prop='prop') - self.assertRaises(CommandNotFound, self.call, cp, 'PROBE', cmd='command_1', document='testdocument', guid='guid', prop='prop') - self.call(cp, 'PROBE', cmd='command_2', document='testdocument', guid='guid', prop='prop') - self.assertRaises(CommandNotFound, self.call, cp, 'PROBE', cmd='command_2', document='fakedocument', guid='guid', prop='prop') - - self.assertEqual([ - ('command_1', {}), - ('command_2', {}), - ], - calls) - - def test_AccessLevel(self): - calls = [] - - class TestCommandsProcessor(CommandsProcessor): - - @volume_command(method='PROBE', cmd='all') - def all(self): - pass - - @volume_command(method='PROBE', cmd='system', access_level=env.ACCESS_SYSTEM) - def system(self): - pass - - @volume_command(method='PROBE', cmd='local', access_level=env.ACCESS_LOCAL) - def local(self): - pass - - @volume_command(method='PROBE', cmd='remote', access_level=env.ACCESS_REMOTE) - def remote(self): - pass - - cp = TestCommandsProcessor() - - self.call(cp, 'PROBE', cmd='all', access_level=env.ACCESS_REMOTE) - self.call(cp, 'PROBE', cmd='all', access_level=env.ACCESS_LOCAL) - self.call(cp, 'PROBE', cmd='all', access_level=env.ACCESS_SYSTEM) - - self.call(cp, 'PROBE', cmd='remote', access_level=env.ACCESS_REMOTE) - self.assertRaises(Forbidden, self.call, cp, 'PROBE', cmd='remote', access_level=env.ACCESS_LOCAL) - self.assertRaises(Forbidden, self.call, cp, 'PROBE', cmd='remote', access_level=env.ACCESS_SYSTEM) - - self.assertRaises(Forbidden, self.call, cp, 'PROBE', cmd='local', access_level=env.ACCESS_REMOTE) - self.call(cp, 'PROBE', cmd='local', access_level=env.ACCESS_LOCAL) - self.assertRaises(Forbidden, self.call, cp, 'PROBE', cmd='local', access_level=env.ACCESS_SYSTEM) - - self.assertRaises(Forbidden, self.call, cp, 'PROBE', cmd='system', access_level=env.ACCESS_REMOTE) - self.assertRaises(Forbidden, self.call, cp, 'PROBE', cmd='system', access_level=env.ACCESS_LOCAL) - self.call(cp, 'PROBE', cmd='system', access_level=env.ACCESS_SYSTEM) - - def test_ParentClasses(self): - calls = [] - - class Parent(object): - - @volume_command(method='PROBE') - def probe(self): - return 'probe' - - class TestCommandsProcessor(CommandsProcessor, Parent): - pass - - cp = TestCommandsProcessor() - self.assertEqual('probe', self.call(cp, 'PROBE')) - - def test_OverrideInChildClass(self): - calls = [] - - class Parent(CommandsProcessor): - - @volume_command(method='PROBE') - def probe(self): - return 'probe-1' - - @volume_command(method='COMMON') - def common(self): - return 'common' - - class Child(Parent): - - @volume_command(method='PROBE') - def probe(self): - return 'probe-2' - - @volume_command(method='PARTICULAR') - def particular(self): - return 'particular' - - cp = Child() - self.assertEqual('probe-2', self.call(cp, 'PROBE')) - self.assertEqual('common', self.call(cp, 'COMMON')) - self.assertEqual('particular', self.call(cp, 'PARTICULAR')) - - def test_RequestRead(self): - - class Stream(object): - - def __init__(self, value): - self.pos = 0 - self.value = value - - def read(self, size): - assert self.pos + size <= len(self.value) - result = self.value[self.pos:self.pos + size] - self.pos += size - return result - - request = Request() - request.content_stream = Stream('123') - request.content_length = len(request.content_stream.value) - self.assertEqual('123', request.read()) - self.assertEqual('', request.read()) - self.assertEqual('', request.read(10)) - - request = Request() - request.content_stream = Stream('123') - request.content_length = len(request.content_stream.value) - self.assertEqual('123', request.read(10)) - - request = Request() - request.content_stream = Stream('123') - request.content_length = len(request.content_stream.value) - self.assertEqual('1', request.read(1)) - self.assertEqual('2', request.read(1)) - self.assertEqual('3', request.read()) - - def test_Arguments(self): - - class TestCommandsProcessor(CommandsProcessor): - - @volume_command(method='PROBE', arguments={'arg_int': to_int, 'arg_list': to_list}) - def probe(self, arg_int=None, arg_list=None): - return arg_int, arg_list - - cp = TestCommandsProcessor() - - self.assertEqual((None, None), self.call(cp, 'PROBE')) - self.assertEqual((-1, [-2, None]), self.call(cp, 'PROBE', arg_int=-1, arg_list=[-2, None])) - self.assertEqual((4, [' foo', ' bar ', ' ']), self.call(cp, 'PROBE', arg_int='4', arg_list=' foo, bar , ')) - self.assertEqual((None, ['foo']), self.call(cp, 'PROBE', arg_list='foo')) - self.assertEqual((None, []), self.call(cp, 'PROBE', arg_list='')) - self.assertEqual((None, [' ']), self.call(cp, 'PROBE', arg_list=' ')) - self.assertEqual((0, None), self.call(cp, 'PROBE', arg_int='')) - self.assertRaises(RuntimeError, self.call, cp, 'PROBE', arg_int=' ') - self.assertRaises(RuntimeError, self.call, cp, 'PROBE', arg_int='foo') - - def test_PassKwargs(self): - - class TestCommandsProcessor(CommandsProcessor): - - @volume_command(method='PROBE') - def probe(self, arg, request, response, **kwargs): - return arg, dict(request), dict(response), kwargs - - cp = TestCommandsProcessor() - - self.assertEqual( - (None, {'method': 'PROBE'}, {}, {}), - self.call(cp, 'PROBE')) - self.assertEqual( - (1, {'method': 'PROBE', 'arg': 1}, {}, {}), - self.call(cp, 'PROBE', arg=1)) - self.assertEqual( - (None, {'method': 'PROBE', 'foo': 'bar'}, {}, {}), - self.call(cp, 'PROBE', foo='bar')) - self.assertEqual( - (-2, {'method': 'PROBE', 'foo': 'bar', 'arg': -2}, {}, {}), - self.call(cp, 'PROBE', foo='bar', arg=-2)) - - def test_PrePost(self): - - class ParentCommandsProcessor(CommandsProcessor): - - @db.volume_command_pre(method='PROBE') - def command_pre1(self, request): - request['probe'].append('pre1') - - @db.volume_command_pre(method='PROBE') - def command_pre2(self, request): - request['probe'].append('pre2') - - @db.volume_command_post(method='PROBE') - def command_post1(self, request, response, result): - request['probe'].append('post1') - response['probe'].append('post1') - return result + 1 - - @db.volume_command_post(method='PROBE') - def command_post2(self, request, response, result): - request['probe'].append('post2') - response['probe'].append('post2') - return result + 1 - - class TestCommandsProcessor(ParentCommandsProcessor): - - @db.volume_command_pre(method='PROBE') - def command_pre3(self, request): - request['probe'].append('pre3') - - @db.volume_command_pre(method='PROBE') - def command_pre4(self, request): - request['probe'].append('pre4') - - @db.volume_command(method='PROBE') - def command(self, request): - request['probe'].append('cmd') - response['probe'].append('cmd') - return 1 - - @db.volume_command_post(method='PROBE') - def command_post3(self, request, response, result): - request['probe'].append('post3') - response['probe'].append('post3') - return result + 1 - - @db.volume_command_post(method='PROBE') - def command_post4(self, request, response, result): - request['probe'].append('post4') - response['probe'].append('post4') - return result + 1 - - cp = TestCommandsProcessor() - - request = db.Request(method='PROBE', probe=[]) - response = db.Response(probe=[]) - self.assertEqual(5, cp.call(request, response)) - self.assertEqual(['pre1', 'pre2', 'pre3', 'pre4', 'cmd', 'post1', 'post2', 'post3', 'post4'], request['probe']) - self.assertEqual(['cmd', 'post1', 'post2', 'post3', 'post4'], response['probe']) - - def test_PrePostCallbackLess(self): - - class TestCommandsProcessor(CommandsProcessor): - - @db.volume_command_pre(method='PROBE') - def command_pre(self, request): - request['probe'].append('pre') - - def super_call(self, request, response): - request['probe'].append('cmd') - response['probe'].append('cmd') - return 1 - - @db.volume_command_post(method='PROBE') - def command_post(self, request, response, result): - request['probe'].append('post') - response['probe'].append('post') - return result + 1 - - cp = TestCommandsProcessor() - - request = db.Request(method='PROBE', probe=[]) - response = db.Response(probe=[]) - self.assertEqual(2, cp.call(request, response)) - self.assertEqual(['pre', 'cmd', 'post'], request['probe']) - self.assertEqual(['cmd', 'post'], response['probe']) - - def test_SubCall(self): - - class TestCommandsProcessor(CommandsProcessor): - - @db.volume_command(method='PROBE') - def command1(self, request): - return request.call('PROBE', cmd='command2') - - @db.volume_command(method='PROBE') - def command2(self, request): - return {'access_level': request.access_level, 'accept_language': request.accept_language} - - cp = TestCommandsProcessor() - - request = db.Request(method='PROBE') - request.access_level = -1 - request.accept_language = 'foo' - self.assertEqual({ - 'access_level': -1, - 'accept_language': 'foo', - }, - cp.call(request, db.Response())) - - def test_Routes(self): - calls = [] - - class BaseRoutes(CommandsProcessor): - - @route('GET', '/foo') - def route1(self, request, response): - return 'route1' - - class Routes(BaseRoutes): - - @route('PUT', '/foo') - def route2(self, request, response): - return 'route2' - - @route('GET', '/bar') - def route3(self, request, response): - return 'route3' - - def call(self, request, response): - try: - return CommandsProcessor.call(self, request, response) - except CommandNotFound: - return 'default' - - cp = Routes() - request = Request() - - request.path = [] - request['method'] = 'GET' - self.assertEqual('default', cp.call(request, db.Response())) - - request.path = ['foo'] - request['method'] = 'GET' - self.assertEqual('route1', cp.call(request, db.Response())) - - request.path = ['foo'] - request['method'] = 'PUT' - self.assertEqual('route2', cp.call(request, db.Response())) - - request.path = ['foo'] - request['method'] = 'POST' - self.assertEqual('default', cp.call(request, db.Response())) - - request.path = ['bar', 'foo', 'probe'] - request['method'] = 'GET' - self.assertEqual('route3', cp.call(request, db.Response())) - - def call(self, cp, method, document=None, guid=None, prop=None, - access_level=env.ACCESS_REMOTE, **kwargs): - - class TestRequest(Request): - - content_stream = None - content_length = 0 - - request = TestRequest(**kwargs) - request['method'] = method - request.access_level = access_level - if document: - request['document'] = document - if guid: - request['guid'] = guid - if prop: - request['prop'] = prop - if 'content_stream' in request: - request.content_stream = request.pop('content_stream') - request.content_length = len(request.content_stream.getvalue()) - - self.response = Response() - return cp.call(request, self.response) - - -if __name__ == '__main__': - tests.main() diff --git a/tests/units/db/env.py b/tests/units/db/env.py deleted file mode 100755 index 953271e..0000000 --- a/tests/units/db/env.py +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env python -# sugar-lint: disable - -import copy -from os.path import exists - -from __init__ import tests - -from sugar_network import toolkit -from sugar_network.db import env - - -class EnvTest(tests.Test): - - def test_gettext(self): - # Fallback to default lang - toolkit._default_lang = 'default' - self.assertEqual('foo', env.gettext({'lang': 'foo', 'default': 'bar'}, 'lang')) - self.assertEqual('bar', env.gettext({'lang': 'foo', 'default': 'bar'}, 'fake')) - - # Exact accept_language - self.assertEqual('', env.gettext(None, 'lang')) - self.assertEqual('foo', env.gettext('foo', 'lang')) - self.assertEqual('foo', env.gettext({'lang': 'foo', 'fake': 'bar', 'default': 'default'}, 'lang')) - self.assertEqual('foo', env.gettext({'lang': 'foo', 'fake': 'bar', 'default': 'default'}, ['lang', 'fake'])) - self.assertEqual('bar', env.gettext({'lang': 'foo', 'fake': 'bar', 'default': 'default'}, ['fake', 'lang'])) - - # Last resort - self.assertEqual('foo', env.gettext({'1': 'foo', '2': 'bar'}, 'fake')) - - # Primed accept_language - self.assertEqual('foo', env.gettext({'1': 'foo', '2': 'bar', 'default': 'default'}, '1-a')) - - # Primed i18n value - self.assertEqual('bar', env.gettext({'1-a': 'foo', '1': 'bar', 'default': 'default'}, '1-b')) - self.assertEqual('foo', env.gettext({'1-a': 'foo', '2': 'bar', 'default': 'default'}, '1-b')) - - def test_gettext_EnAsTheLastResort(self): - toolkit._default_lang = 'en-us' - self.assertEqual('right', env.gettext({'a': 'wrong', 'en': 'right'}, 'probe')) - self.assertEqual('exact', env.gettext({'a': 'wrong', 'en': 'right', 'probe': 'exact'}, 'probe')) - - -if __name__ == '__main__': - tests.main() diff --git a/tests/units/db/index.py b/tests/units/db/index.py index b94675a..9b7d130 100755 --- a/tests/units/db/index.py +++ b/tests/units/db/index.py @@ -11,9 +11,11 @@ from os.path import exists from __init__ import tests from sugar_network import toolkit -from sugar_network.db import index, env -from sugar_network.db.metadata import Metadata, IndexedProperty, GUID_PREFIX +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.directory import _Query +from sugar_network.toolkit.router import ACL from sugar_network.toolkit import coroutine @@ -69,8 +71,8 @@ class IndexTest(tests.Test): ([], 0), db._find(reply=['key'])) - def test_IndexByReprcast(self): - db = Index({'key': IndexedProperty('key', 1, 'K', reprcast=lambda x: "foo" + x)}) + def test_IndexByFmt(self): + db = Index({'key': IndexedProperty('key', 1, 'K', fmt=lambda x: "foo" + x)}) db.store('1', {'key': 'bar'}) @@ -87,7 +89,7 @@ class IndexTest(tests.Test): [], db._find(key='fake', reply=['key'])[0]) - def test_IndexByReprcastGenerator(self): + def test_IndexByFmtGenerator(self): def iterate(value): if value != 'fake': @@ -95,7 +97,7 @@ class IndexTest(tests.Test): yield 'bar' yield value - db = Index({'key': IndexedProperty('key', 1, 'K', reprcast=iterate)}) + db = Index({'key': IndexedProperty('key', 1, 'K', fmt=iterate)}) db.store('1', {'key': 'value'}) self.assertEqual( @@ -497,7 +499,7 @@ class IndexTest(tests.Test): db = Index({}, lambda: commits.append(True)) coroutine.dispatch() - env.index_flush_threshold.value = 1 + index.index_flush_threshold.value = 1 db.store('1', {}) coroutine.dispatch() db.store('2', {}) @@ -510,7 +512,7 @@ class IndexTest(tests.Test): del commits[:] db = Index({}, lambda: commits.append(True)) coroutine.dispatch() - env.index_flush_threshold.value = 2 + index.index_flush_threshold.value = 2 db.store('4', {}) coroutine.dispatch() db.store('5', {}) @@ -525,8 +527,8 @@ class IndexTest(tests.Test): db.close() def test_FlushTimeout(self): - env.index_flush_threshold.value = 0 - env.index_flush_timeout.value = 1 + index.index_flush_threshold.value = 0 + index.index_flush_timeout.value = 1 commits = [] @@ -557,7 +559,7 @@ class IndexTest(tests.Test): self.assertEqual(2, len(commits)) def test_DoNotMissImmediateCommitEvent(self): - env.index_flush_threshold.value = 1 + index.index_flush_threshold.value = 1 commits = [] db = Index({}, lambda: commits.append(True)) @@ -748,6 +750,25 @@ class IndexTest(tests.Test): ]), db._find(prop=['a', '-b', 'c'], reply=['guid'])[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)]) + + 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)]) + + prop = Property('prop', fmt=lambda x: x.keys()) + self.assertEqual(['a', '2'], [i for i in _fmt_prop_value(prop, {'a': 1, 2: 'b'})]) + class Index(index.IndexWriter): @@ -759,7 +780,7 @@ class Index(index.IndexWriter): metadata = Metadata(Index) metadata.update(props) metadata['guid'] = IndexedProperty('guid', - permissions=env.ACCESS_CREATE | env.ACCESS_READ, slot=0, + acl=ACL.CREATE | ACL.READ, slot=0, prefix=GUID_PREFIX) index.IndexWriter.__init__(self, tests.tmpdir + '/index', metadata, *args) diff --git a/tests/units/db/metadata.py b/tests/units/db/metadata.py deleted file mode 100755 index 64c08db..0000000 --- a/tests/units/db/metadata.py +++ /dev/null @@ -1,125 +0,0 @@ -#!/usr/bin/env python -# sugar-lint: disable - -from __init__ import tests - -from sugar_network.db.metadata import Property - - -class MetadataTest(tests.Test): - - def test_Property_decode(self): - prop = Property('prop', typecast=int) - self.assertEqual(1, prop.decode(1)) - self.assertEqual(1, prop.decode(1.1)) - self.assertEqual(1, prop.decode('1')) - self.assertRaises(ValueError, prop.decode, '1.0') - self.assertRaises(ValueError, prop.decode, '') - self.assertRaises(ValueError, prop.decode, None) - - prop = Property('prop', typecast=float) - self.assertEqual(1.0, prop.decode(1)) - self.assertEqual(1.1, prop.decode(1.1)) - self.assertEqual(1.0, prop.decode('1')) - self.assertEqual(1.1, prop.decode('1.1')) - self.assertRaises(ValueError, prop.decode, '') - self.assertRaises(ValueError, prop.decode, None) - - prop = Property('prop', typecast=bool) - self.assertEqual(False, prop.decode(0)) - self.assertEqual(True, prop.decode(1)) - self.assertEqual(True, prop.decode(1.1)) - self.assertEqual(True, prop.decode('1')) - self.assertEqual(True, prop.decode('A')) - self.assertEqual(False, prop.decode('')) - self.assertRaises(ValueError, prop.decode, None) - - prop = Property('prop', typecast=[int]) - self.assertEqual((1,), prop.decode(1)) - self.assertRaises(ValueError, prop.decode, None) - self.assertRaises(ValueError, prop.decode, '') - self.assertEqual((), prop.decode([])) - self.assertEqual((123,), prop.decode('123')) - self.assertRaises(ValueError, prop.decode, 'a') - self.assertEqual((123, 4, 5), prop.decode(['123', 4, 5.6])) - - prop = Property('prop', typecast=[1, 2]) - self.assertRaises(ValueError, prop.decode, 0) - self.assertRaises(ValueError, prop.decode, None) - self.assertRaises(ValueError, prop.decode, '') - self.assertRaises(ValueError, prop.decode, 'A') - self.assertEqual(1, prop.decode(1)) - self.assertEqual(2, prop.decode(2)) - - prop = Property('prop', typecast=[[True, False, 'probe']]) - self.assertRaises(ValueError, prop.decode, None) - self.assertEqual((0, ), prop.decode(0)) - self.assertRaises(ValueError, prop.decode, 'A') - self.assertEqual((True, ), prop.decode(True)) - self.assertEqual((False, ), prop.decode(False)) - self.assertRaises(ValueError, prop.decode, [3]) - self.assertRaises(ValueError, prop.decode, ['A']) - self.assertRaises(ValueError, prop.decode, '') - self.assertEqual((), prop.decode([])) - self.assertEqual((True,), prop.decode([True])) - self.assertEqual((False,), prop.decode([False])) - self.assertEqual((True, False, True), prop.decode([True, False, True])) - self.assertEqual((True, False, 'probe'), prop.decode([True, False, 'probe'])) - self.assertRaises(ValueError, prop.decode, [True, None]) - - prop = Property('prop', typecast=[str]) - self.assertEqual(('',), prop.decode('')) - self.assertEqual(('',), prop.decode([''])) - self.assertEqual((), prop.decode([])) - - prop = Property('prop', typecast=[]) - self.assertRaises(ValueError, prop.decode, None) - self.assertEqual(('',), prop.decode('')) - self.assertEqual(('',), prop.decode([''])) - self.assertEqual((), prop.decode([])) - self.assertEqual(('0',), prop.decode(0)) - self.assertEqual(('',), prop.decode('')) - self.assertEqual(('foo',), prop.decode('foo')) - - prop = Property('prop', typecast=[['A', 'B', 'C']]) - self.assertRaises(ValueError, prop.decode, '') - self.assertRaises(ValueError, prop.decode, ['']) - self.assertEqual((), prop.decode([])) - self.assertEqual(('A', 'B', 'C'), prop.decode(['A', 'B', 'C'])) - self.assertRaises(ValueError, prop.decode, ['a']) - self.assertRaises(ValueError, prop.decode, ['A', 'x']) - - prop = Property('prop', typecast=[frozenset(['A', 'B', 'C'])]) - self.assertEqual(('A', 'B', 'C'), prop.decode(['A', 'B', 'C'])) - - prop = Property('prop', typecast=lambda x: x + 1) - self.assertEqual(1, prop.decode(0)) - - def test_Property_to_string(self): - prop = Property('prop', typecast=int) - self.assertEqual(['0'], prop.to_string(0)) - self.assertEqual(['1'], prop.to_string(1)) - - prop = Property('prop', typecast=float) - self.assertEqual(['0'], prop.to_string(0)) - self.assertEqual(['1.1'], prop.to_string(1.1)) - - prop = Property('prop', typecast=bool) - self.assertEqual(['1'], prop.to_string(True)) - self.assertEqual(['0'], prop.to_string(False)) - - prop = Property('prop', typecast=[int]) - self.assertEqual(['0', '1'], prop.to_string([0, 1])) - - prop = Property('prop', typecast=[1, 2]) - self.assertEqual(['2', '1'], prop.to_string([2, 1])) - - prop = Property('prop', typecast=[[True, 0, 'probe']]) - self.assertEqual(['probe', '1', '0'], prop.to_string(['probe', True, 0])) - - prop = Property('prop', reprcast=lambda x: x.keys()) - self.assertEqual(['a', '2'], prop.to_string({'a': 1, 2: 'b'})) - - -if __name__ == '__main__': - tests.main() diff --git a/tests/units/db/migrate.py b/tests/units/db/migrate.py index 89e75c8..773a45d 100755 --- a/tests/units/db/migrate.py +++ b/tests/units/db/migrate.py @@ -8,7 +8,6 @@ from os.path import exists, lexists from __init__ import tests from sugar_network import db -from sugar_network.db import document, env from sugar_network.db import directory as directory_ from sugar_network.db.directory import Directory from sugar_network.db.index import IndexWriter @@ -18,7 +17,7 @@ class MigrateTest(tests.Test): def test_MissedProps(self): - class Document(document.Document): + class Document(db.Resource): @db.indexed_property(prefix='P') def prop1(self, value): @@ -52,7 +51,7 @@ class MigrateTest(tests.Test): def test_ConvertToJson(self): - class Document(document.Document): + class Document(db.Resource): @db.indexed_property(prefix='P', default='value') def prop(self, value): diff --git a/tests/units/db/document.py b/tests/units/db/resource.py index e25ee29..ed37664 100755 --- a/tests/units/db/document.py +++ b/tests/units/db/resource.py @@ -18,18 +18,19 @@ import gobject from __init__ import tests from sugar_network import db -from sugar_network.db import document, storage, env, index +from sugar_network.db import storage, index 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.util import Sequence +from sugar_network.toolkit.router import ACL +from sugar_network.toolkit import Sequence -class DocumentTest(tests.Test): +class ResourceTest(tests.Test): def test_ActiveProperty_Slotted(self): - class Document(document.Document): + class Document(db.Resource): @db.indexed_property(slot=1) def slotted(self, value): @@ -54,7 +55,7 @@ class DocumentTest(tests.Test): def test_ActiveProperty_SlottedIUnique(self): - class Document(document.Document): + class Document(db.Resource): @db.indexed_property(slot=1) def prop_1(self, value): @@ -68,7 +69,7 @@ class DocumentTest(tests.Test): def test_ActiveProperty_Terms(self): - class Document(document.Document): + class Document(db.Resource): @db.indexed_property(prefix='T') def term(self, value): @@ -94,7 +95,7 @@ class DocumentTest(tests.Test): def test_ActiveProperty_TermsUnique(self): - class Document(document.Document): + class Document(db.Resource): @db.indexed_property(prefix='P') def prop_1(self, value): @@ -108,7 +109,7 @@ class DocumentTest(tests.Test): def test_ActiveProperty_FullTextSearch(self): - class Document(document.Document): + class Document(db.Resource): @db.indexed_property(full_text=False, slot=1) def no(self, value): @@ -129,7 +130,7 @@ class DocumentTest(tests.Test): def test_update(self): - class Document(document.Document): + class Document(db.Resource): @db.indexed_property(slot=1) def prop_1(self, value): @@ -153,7 +154,7 @@ class DocumentTest(tests.Test): def test_delete(self): - class Document(document.Document): + class Document(db.Resource): @db.indexed_property(prefix='P') def prop(self, value): @@ -186,7 +187,7 @@ class DocumentTest(tests.Test): def test_populate(self): - class Document(document.Document): + class Document(db.Resource): @db.indexed_property(slot=1) def prop(self, value): @@ -232,7 +233,7 @@ class DocumentTest(tests.Test): def test_populate_IgnoreBadDocuments(self): - class Document(document.Document): + class Document(db.Resource): @db.indexed_property(slot=1) def prop(self, value): @@ -273,7 +274,7 @@ class DocumentTest(tests.Test): def test_create_with_guid(self): - class Document(document.Document): + class Document(db.Resource): @db.indexed_property(slot=1) def prop(self, value): @@ -293,7 +294,7 @@ class DocumentTest(tests.Test): def test_seqno(self): - class Document(document.Document): + class Document(db.Resource): @db.indexed_property(slot=1) def prop(self, value): @@ -333,7 +334,7 @@ class DocumentTest(tests.Test): def test_diff(self): - class Document(document.Document): + class Document(db.Resource): @db.indexed_property(slot=1) def prop(self, value): @@ -434,9 +435,9 @@ class DocumentTest(tests.Test): def test_diff_IgnoreCalcProps(self): - class Document(document.Document): + class Document(db.Resource): - @db.indexed_property(slot=1, permissions=db.ACCESS_PUBLIC | db.ACCESS_CALC) + @db.indexed_property(slot=1, acl=ACL.PUBLIC | ACL.CALC) def prop(self, value): return value @@ -465,7 +466,7 @@ class DocumentTest(tests.Test): def test_diff_Exclude(self): - class Document(document.Document): + class Document(db.Resource): @db.indexed_property(slot=1) def prop(self, value): @@ -499,7 +500,7 @@ class DocumentTest(tests.Test): URL = 'http://src.sugarlabs.org/robots.txt' URL_content = urllib2.urlopen(URL).read() - class Document(document.Document): + class Document(db.Resource): @db.blob_property() def blob(self, value): @@ -528,7 +529,7 @@ class DocumentTest(tests.Test): def test_diff_Filter(self): - class Document(document.Document): + class Document(db.Resource): @db.indexed_property(prefix='P') def prop(self, value): @@ -555,7 +556,7 @@ class DocumentTest(tests.Test): def test_diff_GroupBy(self): - class Document(document.Document): + class Document(db.Resource): @db.indexed_property(slot=1, prefix='P') def prop(self, value): @@ -584,7 +585,7 @@ class DocumentTest(tests.Test): def test_merge_New(self): - class Document(document.Document): + class Document(db.Resource): @db.indexed_property(slot=1) def prop(self, value): @@ -650,7 +651,7 @@ class DocumentTest(tests.Test): def test_merge_Update(self): - class Document(document.Document): + class Document(db.Resource): @db.blob_property() def blob(self, value): @@ -729,7 +730,7 @@ class DocumentTest(tests.Test): def test_merge_SeqnoLessMode(self): - class Document(document.Document): + class Document(db.Resource): @db.indexed_property(slot=1) def prop(self, value): @@ -788,7 +789,7 @@ class DocumentTest(tests.Test): def test_merge_AvoidCalculatedBlobs(self): - class Document(document.Document): + class Document(db.Resource): @db.blob_property() def blob(self, value): @@ -810,7 +811,7 @@ class DocumentTest(tests.Test): def test_merge_Blobs(self): - class Document(document.Document): + class Document(db.Resource): @db.blob_property() def blob(self, value): @@ -847,7 +848,7 @@ class DocumentTest(tests.Test): def test_wipe(self): - class Document(document.Document): + class Document(db.Resource): pass directory = Directory(tests.tmpdir, Document, IndexWriter) diff --git a/tests/units/db/router.py b/tests/units/db/router.py deleted file mode 100755 index 5147c71..0000000 --- a/tests/units/db/router.py +++ /dev/null @@ -1,533 +0,0 @@ -#!/usr/bin/env python -# sugar-lint: disable - -import re -import os -import time -import json -import urllib2 -import hashlib -import tempfile -from email.utils import formatdate -from cStringIO import StringIO -from os.path import exists - -from __init__ import tests, src_root - -from sugar_network import db, node, static, toolkit -from sugar_network.db.router import Router, _Request, _parse_accept_language, route, _filename -from sugar_network.toolkit import util, default_lang, http -from sugar_network.resources.user import User -from sugar_network.resources.volume import Volume, Resource -from sugar_network import client as local - - -class RouterTest(tests.Test): - - def test_StreamedResponse(self): - - class CommandsProcessor(db.CommandsProcessor): - - @db.volume_command() - def get_stream(self, response): - return StringIO('stream') - - cp = CommandsProcessor() - router = Router(cp) - - response = router({ - 'PATH_INFO': '/', - 'REQUEST_METHOD': 'GET', - }, - lambda *args: None) - self.assertEqual('stream', ''.join([i for i in response])) - - def test_EmptyResponse(self): - - class CommandsProcessor(db.CommandsProcessor): - - @db.volume_command(cmd='1', mime_type='application/octet-stream') - def get_binary(self, response): - pass - - @db.volume_command(cmd='2', mime_type='application/json') - def get_json(self, response): - pass - - @db.volume_command(cmd='3') - def no_get(self, response): - pass - - cp = CommandsProcessor() - router = Router(cp) - - response = router({ - 'PATH_INFO': '/', - 'REQUEST_METHOD': 'GET', - 'QUERY_STRING': 'cmd=1', - }, - lambda *args: None) - self.assertEqual('', ''.join([i for i in response])) - - response = router({ - 'PATH_INFO': '/', - 'REQUEST_METHOD': 'GET', - 'QUERY_STRING': 'cmd=2', - }, - lambda *args: None) - self.assertEqual('null', ''.join([i for i in response])) - - response = router({ - 'PATH_INFO': '/', - 'REQUEST_METHOD': 'GET', - 'QUERY_STRING': 'cmd=3', - }, - lambda *args: None) - self.assertEqual('', ''.join([i for i in response])) - - def test_StatusWOResult(self): - - class Status(http.Status): - status = '001 Status' - headers = {'status-header': 'value'} - - class CommandsProcessor(db.CommandsProcessor): - - @db.volume_command(method='GET') - def get(self, response): - raise Status('Status-Error') - - router = Router(CommandsProcessor()) - - response = [] - reply = router({ - 'PATH_INFO': '/', - 'REQUEST_METHOD': 'GET', - }, - lambda status, headers: response.extend([status, dict(headers)])) - error = json.dumps({'request': '/', 'error': 'Status-Error'}) - self.assertEqual(error, ''.join([i for i in reply])) - self.assertEqual([ - '001 Status', - {'content-length': str(len(error)), 'content-type': 'application/json', 'status-header': 'value'}, - ], - response) - - def test_StatusWResult(self): - - class Status(http.Status): - status = '001 Status' - headers = {'status-header': 'value'} - result = 'result' - - class CommandsProcessor(db.CommandsProcessor): - - @db.volume_command(method='GET') - def get(self, response): - raise Status('Status-Error') - - router = Router(CommandsProcessor()) - - response = [] - reply = router({ - 'PATH_INFO': '/', - 'REQUEST_METHOD': 'GET', - }, - lambda status, headers: response.extend([status, dict(headers)])) - error = 'result' - self.assertEqual(error, ''.join([i for i in reply])) - self.assertEqual([ - '001 Status', - {'content-length': str(len(error)), 'status-header': 'value'}, - ], - response) - - def test_StatusPass(self): - - class StatusPass(http.StatusPass): - status = '001 StatusPass' - headers = {'statuspass-header': 'value'} - result = 'result' - - class CommandsProcessor(db.CommandsProcessor): - - @db.volume_command(method='GET') - def get(self, response): - raise StatusPass('Status-Error') - - router = Router(CommandsProcessor()) - - response = [] - reply = router({ - 'PATH_INFO': '/', - 'REQUEST_METHOD': 'GET', - }, - lambda status, headers: response.extend([status, dict(headers)])) - error = '' - self.assertEqual(error, ''.join([i for i in reply])) - self.assertEqual([ - '001 StatusPass', - {'content-length': str(len(error)), 'statuspass-header': 'value'}, - ], - response) - - def test_BlobsRedirects(self): - URL = 'http://sugarlabs.org' - - class CommandsProcessor(db.CommandsProcessor): - - @db.volume_command(method='GET') - def get(self, response): - return db.PropertyMetadata(url=URL) - - router = Router(CommandsProcessor()) - - response = [] - reply = router({ - 'PATH_INFO': '/', - 'REQUEST_METHOD': 'GET', - }, - lambda status, headers: response.extend([status, dict(headers)])) - error = '' - self.assertEqual(error, ''.join([i for i in reply])) - self.assertEqual([ - '303 See Other', - {'content-length': '0', 'location': URL}, - ], - response) - - def test_LastModified(self): - - class CommandsProcessor(db.CommandsProcessor): - - @db.volume_command(method='GET') - def get(self, request): - request.response.last_modified = 10 - return 'ok' - - router = Router(CommandsProcessor()) - - response = [] - reply = router({ - 'PATH_INFO': '/', - 'REQUEST_METHOD': 'GET', - }, - lambda status, headers: response.extend([status, dict(headers)])) - result = 'ok' - self.assertEqual(result, ''.join([i for i in reply])) - self.assertEqual([ - '200 OK', - {'last-modified': formatdate(10, localtime=False, usegmt=True), 'content-length': str(len(result))}, - ], - response) - - def test_IfModifiedSince(self): - - class CommandsProcessor(db.CommandsProcessor): - - @db.volume_command(method='GET') - def get(self, request): - if not request.if_modified_since or request.if_modified_since >= 10: - return 'ok' - else: - raise http.NotModified() - - router = Router(CommandsProcessor()) - - response = [] - reply = router({ - 'PATH_INFO': '/', - 'REQUEST_METHOD': 'GET', - }, - lambda status, headers: response.extend([status, dict(headers)])) - result = 'ok' - self.assertEqual(result, ''.join([i for i in reply])) - self.assertEqual([ - '200 OK', - {'content-length': str(len(result))}, - ], - response) - - response = [] - reply = router({ - 'PATH_INFO': '/', - 'REQUEST_METHOD': 'GET', - 'HTTP_IF_MODIFIED_SINCE': formatdate(11, localtime=False, usegmt=True), - }, - lambda status, headers: response.extend([status, dict(headers)])) - result = 'ok' - self.assertEqual(result, ''.join([i for i in reply])) - self.assertEqual([ - '200 OK', - {'content-length': str(len(result))}, - ], - response) - - response = [] - reply = router({ - 'PATH_INFO': '/', - 'REQUEST_METHOD': 'GET', - 'HTTP_IF_MODIFIED_SINCE': formatdate(9, localtime=False, usegmt=True), - }, - lambda status, headers: response.extend([status, dict(headers)])) - result = '' - self.assertEqual(result, ''.join([i for i in reply])) - self.assertEqual([ - '304 Not Modified', - {'content-length': str(len(result))}, - ], - response) - - def test_Request_MultipleQueryArguments(self): - request = _Request({ - 'PATH_INFO': '/', - 'REQUEST_METHOD': 'GET', - 'QUERY_STRING': 'a1=v1&a2=v2&a1=v3&a3=v4&a1=v5&a3=v6', - }) - self.assertEqual( - {'a1': ['v1', 'v3', 'v5'], 'a2': 'v2', 'a3': ['v4', 'v6'], 'method': 'GET'}, - request) - - def test_Register_UrlPath(self): - self.assertEqual( - [], - _Request({'REQUEST_METHOD': 'GET', 'PATH_INFO': ''}).path) - self.assertEqual( - [], - _Request({'REQUEST_METHOD': 'GET', 'PATH_INFO': '/'}).path) - self.assertEqual( - ['foo'], - _Request({'REQUEST_METHOD': 'GET', 'PATH_INFO': 'foo'}).path) - self.assertEqual( - ['foo', 'bar'], - _Request({'REQUEST_METHOD': 'GET', 'PATH_INFO': 'foo/bar'}).path) - self.assertEqual( - ['foo', 'bar'], - _Request({'REQUEST_METHOD': 'GET', 'PATH_INFO': '/foo/bar/'}).path) - self.assertEqual( - ['foo', 'bar'], - _Request({'REQUEST_METHOD': 'GET', 'PATH_INFO': '///foo////bar////'}).path) - - def test_Request_FailOnRelativePaths(self): - self.assertRaises(RuntimeError, _Request, {'REQUEST_METHOD': 'GET', 'PATH_INFO': '..'}) - self.assertRaises(RuntimeError, _Request, {'REQUEST_METHOD': 'GET', 'PATH_INFO': '/..'}) - self.assertRaises(RuntimeError, _Request, {'REQUEST_METHOD': 'GET', 'PATH_INFO': '/../'}) - self.assertRaises(RuntimeError, _Request, {'REQUEST_METHOD': 'GET', 'PATH_INFO': '../bar'}) - self.assertRaises(RuntimeError, _Request, {'REQUEST_METHOD': 'GET', 'PATH_INFO': '/foo/../bar'}) - self.assertRaises(RuntimeError, _Request, {'REQUEST_METHOD': 'GET', 'PATH_INFO': '/foo/..'}) - - def test_parse_accept_language(self): - self.assertEqual( - ['ru', 'en', 'es'], - _parse_accept_language(' ru , en , es')) - self.assertEqual( - ['ru', 'en', 'es'], - _parse_accept_language(' en;q=.4 , ru, es;q=0.1')) - self.assertEqual( - ['ru', 'en', 'es'], - _parse_accept_language('ru;q=1,en;q=1,es;q=0.5')) - self.assertEqual( - ['ru-ru', 'es-br'], - _parse_accept_language('ru-RU,es_BR')) - - def test_StaticFiles(self): - router = Router(db.CommandsProcessor()) - 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_StaticFilesIfModifiedSince(self): - router = Router(db.CommandsProcessor()) - local_path = src_root + '/sugar_network/static/httpdocs/images/missing.png' - mtime = os.stat(local_path).st_mtime - - response = [] - reply = router({ - 'PATH_INFO': '/static/images/missing.png', - 'REQUEST_METHOD': 'GET', - 'HTTP_IF_MODIFIED_SINCE': formatdate(mtime - 1, localtime=False, usegmt=True), - }, - 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(mtime, localtime=False, usegmt=True), - 'content-length': str(len(result)), - 'content-type': 'image/png', - 'content-disposition': 'attachment; filename="missing.png"', - } - ], - response) - - response = [] - reply = router({ - 'PATH_INFO': '/static/images/missing.png', - 'REQUEST_METHOD': 'GET', - 'HTTP_IF_MODIFIED_SINCE': formatdate(mtime, localtime=False, usegmt=True), - }, - lambda status, headers: response.extend([status, dict(headers)])) - result = '' - self.assertEqual(result, ''.join([i for i in reply])) - self.assertEqual([ - '304 Not Modified', - {'content-length': str(len(result))}, - ], - response) - - response = [] - reply = router({ - 'PATH_INFO': '/static/images/missing.png', - 'REQUEST_METHOD': 'GET', - 'HTTP_IF_MODIFIED_SINCE': formatdate(mtime + 1, localtime=False, usegmt=True), - }, - lambda status, headers: response.extend([status, dict(headers)])) - result = '' - self.assertEqual(result, ''.join([i for i in reply])) - self.assertEqual([ - '304 Not Modified', - {'content-length': str(len(result))}, - ], - response) - - def test_JsonpCallback(self): - - class CommandsProcessor(db.CommandsProcessor): - - @db.volume_command(method='GET') - def get(self, request): - return 'ok' - - router = Router(CommandsProcessor()) - - response = [] - reply = router({ - 'PATH_INFO': '/', - 'REQUEST_METHOD': 'GET', - 'QUERY_STRING': 'callback=foo', - }, - lambda status, headers: response.extend([status, dict(headers)])) - result = 'foo("ok");' - self.assertEqual(result, ''.join([i for i in reply])) - self.assertEqual([ - '200 OK', - {'content-length': str(len(result))}, - ], - 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('Foo-3', _filename(['foo', 3], 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): - self.touch(('blob.data', 'value')) - - class CommandsProcessor(db.CommandsProcessor): - - @db.volume_command(method='GET', cmd='1') - def cmd1(self, request): - return db.PropertyMetadata(name='foo', blob='blob.data') - - @db.volume_command(method='GET', cmd='2') - def cmd2(self, request): - return db.PropertyMetadata(filename='foo.bar', blob='blob.data') - - router = Router(CommandsProcessor()) - - response = [] - reply = router({ - 'PATH_INFO': '/', - 'REQUEST_METHOD': 'GET', - 'QUERY_STRING': 'cmd=1', - }, - lambda status, headers: response.extend([status, dict(headers)])) - result = 'value' - self.assertEqual(result, ''.join([i for i in reply])) - self.assertEqual([ - '200 OK', - { - '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"', - } - ], - response) - - response = [] - reply = router({ - 'PATH_INFO': '/', - 'REQUEST_METHOD': 'GET', - 'QUERY_STRING': 'cmd=2', - }, - lambda status, headers: response.extend([status, dict(headers)])) - result = 'value' - self.assertEqual(result, ''.join([i for i in reply])) - self.assertEqual([ - '200 OK', - { - '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.bar"', - } - ], - response) - - def test_DoNotOverrideContentLengthForHEAD(self): - - class CommandsProcessor(db.CommandsProcessor): - - @db.route('HEAD', '/') - def head(self, request, response): - response.content_length = 100 - - router = Router(CommandsProcessor()) - - response = [] - reply = router({ - 'PATH_INFO': '/', - 'REQUEST_METHOD': 'HEAD', - }, - lambda status, headers: response.extend([status, dict(headers)])) - self.assertEqual([], [i for i in reply]) - self.assertEqual([ - '200 OK', - {'content-length': '100'}, - ], - response) - - -if __name__ == '__main__': - tests.main() diff --git a/tests/units/db/routes.py b/tests/units/db/routes.py new file mode 100755 index 0000000..3d2d7f1 --- /dev/null +++ b/tests/units/db/routes.py @@ -0,0 +1,1600 @@ +#!/usr/bin/env python +# sugar-lint: disable + +import os +import sys +import time +import shutil +import hashlib +from cStringIO import StringIO +from email.message import Message +from email.utils import formatdate +from os.path import dirname, join, abspath, exists + +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 + + +class RoutesTest(tests.Test): + + def test_PostDefaults(self): + + class Document(db.Resource): + + @db.stored_property(default='default') + def w_default(self, value): + return value + + @db.stored_property() + def wo_default(self, value): + return value + + @db.indexed_property(slot=1, default='not_stored_default') + def not_stored_default(self, value): + return value + + self.volume = db.Volume(tests.tmpdir, [Document], lambda event: None) + + self.assertRaises(RuntimeError, self.call, 'POST', ['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'])) + + def test_Populate(self): + self.touch( + ('document/1/1/guid', '{"value": "1"}'), + ('document/1/1/ctime', '{"value": 1}'), + ('document/1/1/mtime', '{"value": 1}'), + ('document/1/1/seqno', '{"value": 0}'), + + ('document/2/2/guid', '{"value": "2"}'), + ('document/2/2/ctime', '{"value": 2}'), + ('document/2/2/mtime', '{"value": 2}'), + ('document/2/2/seqno', '{"value": 0}'), + ) + + class Document(db.Resource): + pass + + with db.Volume(tests.tmpdir, [Document], lambda event: None) as volume: + for cls in volume.values(): + for __ in cls.populate(): + pass + self.assertEqual( + sorted(['1', '2']), + sorted([i.guid for i in volume['document'].find()[0]])) + + shutil.rmtree('document/index') + + class Document(db.Resource): + pass + + with db.Volume(tests.tmpdir, [Document], lambda event: None) as volume: + for cls in volume.values(): + for __ in cls.populate(): + pass + self.assertEqual( + sorted(['1', '2']), + sorted([i.guid for i in volume['document'].find()[0]])) + + def test_Commands(self): + + class TestDocument(db.Resource): + + @db.indexed_property(slot=1, default='') + def prop(self, value): + return value + + @db.indexed_property(prefix='L', localized=True, default='') + def localized_prop(self, value): + return value + + self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) + self.volume['testdocument'].create({'guid': 'guid'}) + + self.assertEqual({ + 'total': 1, + 'result': [ + {'guid': 'guid', 'prop': ''}, + ], + }, + self.call('GET', path=['testdocument'], reply=['guid', 'prop'])) + + guid_1 = self.call('POST', path=['testdocument'], content={'prop': 'value_1'}) + assert guid_1 + guid_2 = self.call('POST', path=['testdocument'], content={'prop': 'value_2'}) + assert guid_2 + + self.assertEqual( + sorted([ + {'guid': 'guid', 'prop': ''}, + {'guid': guid_1, 'prop': 'value_1'}, + {'guid': guid_2, 'prop': 'value_2'}, + ]), + sorted(self.call('GET', path=['testdocument'], reply=['guid', 'prop'])['result'])) + + self.call('PUT', path=['testdocument', guid_1], content={'prop': 'value_3'}) + + self.assertEqual( + sorted([ + {'guid': 'guid', 'prop': ''}, + {'guid': guid_1, 'prop': 'value_3'}, + {'guid': guid_2, 'prop': 'value_2'}, + ]), + sorted(self.call('GET', path=['testdocument'], reply=['guid', 'prop'])['result'])) + + self.call('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'])) + + self.assertRaises(http.NotFound, self.call, 'GET', path=['testdocument', guid_2]) + + self.assertEqual( + {'guid': guid_1, 'prop': 'value_3'}, + self.call('GET', path=['testdocument', guid_1], reply=['guid', 'prop'])) + + self.assertEqual( + 'value_3', + self.call('GET', path=['testdocument', guid_1, 'prop'])) + + def test_SetBLOBs(self): + + class TestDocument(db.Resource): + + @db.blob_property() + def blob(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'], content='blob1') + self.assertEqual('blob1', file(self.call('GET', path=['testdocument', guid, 'blob'])['blob']).read()) + + self.call('PUT', path=['testdocument', guid, 'blob'], content_stream=StringIO('blob2')) + self.assertEqual('blob2', file(self.call('GET', path=['testdocument', guid, 'blob'])['blob']).read()) + + self.call('PUT', path=['testdocument', guid, 'blob'], content=None) + self.assertRaises(http.NotFound, self.call, 'GET', path=['testdocument', guid, 'blob']) + + def test_SetBLOBsByMeta(self): + + class TestDocument(db.Resource): + + @db.blob_property(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={}) + + self.assertRaises(RuntimeError, self.call, 'PUT', path=['testdocument', guid, 'blob'], + content={}, content_type='application/json') + self.assertRaises(http.NotFound, self.call, 'GET', path=['testdocument', guid, 'blob']) + + self.touch('file') + self.assertRaises(RuntimeError, self.call, 'PUT', path=['testdocument', guid, 'blob'], + content={'blob': 'file'}, content_type='application/json') + self.assertRaises(http.NotFound, self.call, 'GET', path=['testdocument', guid, 'blob']) + + 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']) + assert 'bar' not in blob + + def test_RemoveBLOBs(self): + + class TestDocument(db.Resource): + + @db.blob_property(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={'blob': 'blob'}) + + self.assertEqual('blob', file(self.call('GET', path=['testdocument', guid, 'blob'])['blob']).read()) + + self.call('PUT', path=['testdocument', guid, 'blob']) + self.assertRaises(http.NotFound, self.call, 'GET', path=['testdocument', guid, 'blob']) + + def test_RemoveTempBLOBFilesOnFails(self): + + class TestDocument(db.Resource): + + @db.blob_property(mime_type='default') + def blob(self, value): + return value + + @blob.setter + def blob(self, value): + raise RuntimeError() + + self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) + guid = self.call('POST', path=['testdocument'], content={}) + + self.assertRaises(RuntimeError, self.call, 'PUT', path=['testdocument', guid, 'blob'], content='probe') + self.assertEqual(0, len(os.listdir('tmp'))) + + def test_SetBLOBsWithMimeType(self): + + class TestDocument(db.Resource): + + @db.blob_property(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={}) + + 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) + + 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) + + def test_GetBLOBs(self): + + class TestDocument(db.Resource): + + @db.blob_property() + def blob(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'], content='blob') + + blob_path = tests.tmpdir + '/testdocument/%s/%s/blob' % (guid[:2], guid) + blob_meta = { + 'seqno': 2, + 'blob': blob_path + '.blob', + 'blob_size': 4, + 'digest': hashlib.sha1('blob').hexdigest(), + 'mime_type': 'application/octet-stream', + 'mtime': int(os.stat(blob_path).st_mtime), + } + + self.assertEqual('blob', file(self.call('GET', path=['testdocument', guid, 'blob'])['blob']).read()) + + self.assertEqual( + {'guid': guid, 'blob': 'http://localhost/testdocument/%s/blob' % guid}, + self.call('GET', path=['testdocument', guid], reply=['guid', 'blob'], host='localhost')) + + self.assertEqual([ + {'guid': guid, 'blob': 'http://localhost/testdocument/%s/blob' % guid}, + ], + self.call('GET', path=['testdocument'], reply=['guid', 'blob'], host='localhost')['result']) + + def test_GetBLOBsByUrls(self): + + class TestDocument(db.Resource): + + @db.blob_property() + def blob(self, value): + return value + + self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) + guid = self.call('POST', path=['testdocument'], content={}) + + self.assertRaises(http.NotFound, self.call, 'GET', path=['testdocument', guid, 'blob']) + self.assertEqual( + {'blob': 'http://127.0.0.1/testdocument/%s/blob' % guid}, + self.call('GET', path=['testdocument', guid], reply=['blob'], host='127.0.0.1')) + self.assertEqual([ + {'blob': 'http://127.0.0.1/testdocument/%s/blob' % guid}, + ], + self.call('GET', path=['testdocument'], reply=['blob'], host='127.0.0.1')['result']) + + self.call('PUT', path=['testdocument', guid, 'blob'], content='file') + self.assertEqual('file', file(self.call('GET', path=['testdocument', guid, 'blob'])['blob']).read()) + self.assertEqual( + {'blob': 'http://127.0.0.1/testdocument/%s/blob' % guid}, + self.call('GET', path=['testdocument', guid], reply=['blob'], host='127.0.0.1')) + self.assertEqual([ + {'blob': 'http://127.0.0.1/testdocument/%s/blob' % guid}, + ], + self.call('GET', path=['testdocument'], reply=['blob'], host='127.0.0.1')['result']) + + self.call('PUT', path=['testdocument', guid, 'blob'], content={'url': 'http://foo'}, + content_type='application/json') + self.assertEqual('http://foo', self.call('GET', path=['testdocument', guid, 'blob'])['url']) + self.assertEqual( + {'blob': 'http://foo'}, + self.call('GET', path=['testdocument', guid], reply=['blob'], host='127.0.0.1')) + self.assertEqual([ + {'blob': 'http://foo'}, + ], + self.call('GET', path=['testdocument'], reply=['blob'], 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() + def blob(self, value): + return value + + self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) + + 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']) + self.assertEqual( + {'blob': 'http://localhost/testdocument/%s/blob' % guid}, + self.call('GET', path=['testdocument', guid], reply=['blob'], host='localhost')) + + def test_Command_ReplyForGET(self): + + class TestDocument(db.Resource): + + @db.indexed_property(slot=1, default='') + def prop(self, value): + return value + + @db.indexed_property(prefix='L', localized=True, 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'}) + + self.assertEqual( + ['guid', 'prop'], + self.call('GET', path=['testdocument', guid], reply=['guid', 'prop']).keys()) + + self.assertEqual( + ['guid'], + self.call('GET', path=['testdocument'])['result'][0].keys()) + + self.assertEqual( + sorted(['guid', 'prop']), + sorted(self.call('GET', path=['testdocument'], reply=['prop', 'guid'])['result'][0].keys())) + + self.assertEqual( + sorted(['prop']), + sorted(self.call('GET', path=['testdocument'], reply=['prop'])['result'][0].keys())) + + def test_DecodeBeforeSetting(self): + + class TestDocument(db.Resource): + + @db.indexed_property(slot=1, typecast=int) + def prop(self, value): + return value + + self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) + + guid = self.call('POST', path=['testdocument'], content={'prop': '-1'}) + self.assertEqual(-1, self.call('GET', path=['testdocument', guid, 'prop'])) + + def test_LocalizedSet(self): + toolkit._default_lang = 'en' + + class TestDocument(db.Resource): + + @db.indexed_property(slot=1, default='') + def prop(self, value): + return value + + @db.indexed_property(prefix='L', localized=True, 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(0, 100, localized_prop='value_raw')[0]]) + + directory.update(guid, {'localized_prop': 'value_raw2'}) + self.assertEqual({'en': 'value_raw2'}, directory.get(guid)['localized_prop']) + self.assertEqual( + [guid], + [i.guid for i in directory.find(0, 100, localized_prop='value_raw2')[0]]) + + guid = self.call('POST', 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(0, 100, localized_prop='value_ru')[0]]) + + self.call('PUT', path=['testdocument', guid], accept_language=['en'], content={'localized_prop': 'value_en'}) + self.assertEqual({'ru': 'value_ru', 'en': 'value_en'}, directory.get(guid)['localized_prop']) + self.assertEqual( + [guid], + [i.guid for i in directory.find(0, 100, localized_prop='value_ru')[0]]) + self.assertEqual( + [guid], + [i.guid for i in directory.find(0, 100, localized_prop='value_en')[0]]) + + def test_LocalizedGet(self): + + class TestDocument(db.Resource): + + @db.indexed_property(slot=1, default='') + def prop(self, value): + return value + + @db.indexed_property(prefix='L', localized=True, default='') + def localized_prop(self, value): + return value + + self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) + directory = self.volume['testdocument'] + + guid = self.call('POST', path=['testdocument'], content={ + 'localized_prop': { + 'ru': 'value_ru', + 'es': 'value_es', + 'en': 'value_en', + }, + }) + + toolkit._default_lang = 'en' + + self.assertEqual( + {'localized_prop': 'value_en'}, + self.call('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'])) + self.assertEqual( + 'value_ru', + self.call('GET', path=['testdocument', guid, 'localized_prop'], accept_language=['ru', 'es'])) + self.assertEqual( + [{'localized_prop': 'value_ru'}], + self.call('GET', path=['testdocument'], accept_language=['foo', 'ru', 'es'], reply=['localized_prop'])['result']) + + self.assertEqual( + {'localized_prop': 'value_ru'}, + self.call('GET', path=['testdocument', guid], accept_language=['ru-RU'], reply=['localized_prop'])) + self.assertEqual( + 'value_ru', + self.call('GET', path=['testdocument', guid, 'localized_prop'], 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']) + + self.assertEqual( + {'localized_prop': 'value_es'}, + self.call('GET', path=['testdocument', guid], accept_language=['es'], reply=['localized_prop'])) + self.assertEqual( + 'value_es', + self.call('GET', path=['testdocument', guid, 'localized_prop'], accept_language=['es', 'ru'])) + self.assertEqual( + [{'localized_prop': 'value_es'}], + self.call('GET', path=['testdocument'], accept_language=['foo', 'es', 'ru'], reply=['localized_prop'])['result']) + + self.assertEqual( + {'localized_prop': 'value_en'}, + self.call('GET', path=['testdocument', guid], accept_language=['fr'], reply=['localized_prop'])) + self.assertEqual( + 'value_en', + self.call('GET', path=['testdocument', guid, 'localized_prop'], accept_language=['fr', 'za'])) + self.assertEqual( + [{'localized_prop': 'value_en'}], + self.call('GET', path=['testdocument'], accept_language=['foo', 'fr', 'za'], reply=['localized_prop'])['result']) + + toolkit._default_lang = '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'])) + self.assertEqual( + 'value_%s' % fallback_lang, + self.call('GET', path=['testdocument', guid, 'localized_prop'], 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']) + + def test_OpenByModuleName(self): + self.touch( + ('foo/bar.py', [ + 'from sugar_network import db', + 'class Bar(db.Resource): pass', + ]), + ('foo/__init__.py', ''), + ) + sys.path.insert(0, '.') + + volume = db.Volume('.', ['foo.bar'], lambda event: None) + assert exists('bar/index') + volume['bar'].find() + volume.close() + + def test_Command_GetBlobSetByUrl(self): + + class TestDocument(db.Resource): + + @db.indexed_property(slot=1, default='') + def prop(self, value): + return value + + @db.blob_property() + def blob(self, value): + return value + + @db.indexed_property(prefix='L', localized=True, 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') + + self.assertEqual( + 'http://sugarlabs.org', + self.call('GET', path=['testdocument', guid, 'blob'])['url']) + + def test_on_create(self): + + class TestDocument(db.Resource): + + @db.indexed_property(slot=1, default='') + def prop(self, value): + return value + + @db.indexed_property(prefix='L', localized=True, default='') + def localized_prop(self, value): + return value + + self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) + + 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) + + def test_on_create_Override(self): + + class Routes(db.Routes): + + def on_create(self, request, props, event): + props['prop'] = 'overriden' + db.Routes.on_create(self, request, props, event) + + class TestDocument(db.Resource): + + @db.indexed_property(slot=1, default='') + def prop(self, value): + return value + + @db.indexed_property(prefix='L', localized=True, default='') + def localized_prop(self, value): + return value + + self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) + + guid = self.call('POST', ['testdocument'], content={'prop': 'foo'}, routes=Routes) + self.assertEqual('overriden', self.volume['testdocument'].get(guid)['prop']) + + self.call('PUT', ['testdocument', guid], content={'prop': 'bar'}, routes=Routes) + self.assertEqual('bar', self.volume['testdocument'].get(guid)['prop']) + + def test_on_update(self): + + class TestDocument(db.Resource): + + @db.indexed_property(slot=1, default='') + def prop(self, value): + return value + + @db.indexed_property(prefix='L', localized=True, 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'] + + time.sleep(1) + + self.call('PUT', path=['testdocument', guid], content={'prop': 'probe'}) + assert self.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): + props['prop'] = 'overriden' + db.Routes.on_update(self, request, props, event) + + class TestDocument(db.Resource): + + @db.indexed_property(slot=1, default='') + def prop(self, value): + return value + + @db.indexed_property(prefix='L', localized=True, default='') + def localized_prop(self, value): + return value + + self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) + + guid = self.call('POST', ['testdocument'], content={'prop': 'foo'}, routes=Routes) + self.assertEqual('foo', self.volume['testdocument'].get(guid)['prop']) + + self.call('PUT', ['testdocument', guid], content={'prop': 'bar'}, routes=Routes) + self.assertEqual('overriden', self.volume['testdocument'].get(guid)['prop']) + + def __test_DoNotPassGuidsForCreate(self): + + class TestDocument(db.Resource): + + @db.indexed_property(slot=1, default='') + def prop(self, value): + return value + + @db.indexed_property(prefix='L', localized=True, 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={}) + assert guid + + def test_seqno(self): + + class Document1(db.Resource): + pass + + class Document2(db.Resource): + pass + + volume = db.Volume(tests.tmpdir, [Document1, Document2], lambda event: None) + + assert not exists('seqno') + self.assertEqual(0, volume.seqno.value) + + volume['document1'].create({'guid': '1'}) + self.assertEqual(1, volume['document1'].get('1')['seqno']) + volume['document2'].create({'guid': '1'}) + self.assertEqual(2, volume['document2'].get('1')['seqno']) + volume['document1'].create({'guid': '2'}) + self.assertEqual(3, volume['document1'].get('2')['seqno']) + volume['document2'].create({'guid': '2'}) + self.assertEqual(4, volume['document2'].get('2')['seqno']) + + self.assertEqual(4, volume.seqno.value) + assert not exists('seqno') + volume.seqno.commit() + assert exists('seqno') + volume = db.Volume(tests.tmpdir, [Document1, Document2], lambda event: None) + self.assertEqual(4, volume.seqno.value) + + def test_Events(self): + db.index_flush_threshold.value = 0 + db.index_flush_timeout.value = 0 + + class Document1(db.Resource): + + @db.indexed_property(slot=1, default='') + def prop(self, value): + pass + + class Document2(db.Resource): + + @db.indexed_property(slot=1, default='') + def prop(self, value): + pass + + @db.blob_property() + def blob(self, value): + return value + + self.touch( + ('document1/1/1/guid', '{"value": "1"}'), + ('document1/1/1/ctime', '{"value": 1}'), + ('document1/1/1/mtime', '{"value": 1}'), + ('document1/1/1/prop', '{"value": ""}'), + ('document1/1/1/seqno', '{"value": 0}'), + ) + + events = [] + volume = db.Volume(tests.tmpdir, [Document1, Document2], lambda event: events.append(event)) + 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[:] + + volume['document1'].create({'guid': 'guid1'}) + volume['document2'].create({'guid': 'guid2'}) + self.assertEqual([ + {'event': 'create', 'resource': 'document1', 'guid': 'guid1'}, + {'event': 'create', 'resource': 'document2', 'guid': 'guid2'}, + ], + events) + del events[:] + + volume['document1'].update('guid1', {'prop': 'foo'}) + volume['document2'].update('guid2', {'prop': 'bar'}) + self.assertEqual([ + {'event': 'update', 'resource': 'document1', 'guid': 'guid1'}, + {'event': 'update', 'resource': 'document2', 'guid': 'guid2'}, + ], + events) + del events[:] + + volume['document1'].delete('guid1') + self.assertEqual([ + {'event': 'delete', 'resource': 'document1', 'guid': 'guid1'}, + ], + events) + del events[:] + + volume['document1'].commit() + mtime1 = int(os.stat('document1/index/mtime').st_mtime) + volume['document2'].commit() + mtime2 = int(os.stat('document2/index/mtime').st_mtime) + + self.assertEqual([ + {'event': 'commit', 'resource': 'document1', 'mtime': mtime1}, + {'event': 'commit', 'resource': 'document2', 'mtime': mtime2}, + ], + events) + + def test_PermissionsNoWrite(self): + + class TestDocument(db.Resource): + + @db.indexed_property(slot=1, default='', acl=ACL.READ) + def prop(self, value): + pass + + @db.blob_property(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={}) + + 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') + + def test_BlobsWritePermissions(self): + + class TestDocument(db.Resource): + + @db.blob_property(acl=ACL.CREATE | ACL.WRITE) + def blob1(self, value): + return value + + @db.blob_property(acl=ACL.CREATE) + def blob2(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], 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 = 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_') + + def test_properties_OverrideGet(self): + + class TestDocument(db.Resource): + + @db.indexed_property(slot=1, default='1') + def prop1(self, value): + return value + + @db.indexed_property(slot=2, default='2') + def prop2(self, value): + return -1 + + @db.blob_property() + 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={}) + self.touch(('new-blob', 'new-blob')) + self.call('PUT', path=['testdocument', guid, 'blob'], content='old-blob') + + self.assertEqual( + 'new-blob', + self.call('GET', path=['testdocument', guid, 'blob'])['blob']) + self.assertEqual( + '1', + self.call('GET', path=['testdocument', guid, 'prop1'])) + self.assertEqual( + -1, + self.call('GET', path=['testdocument', guid, 'prop2'])) + self.assertEqual( + {'prop1': '1', 'prop2': -1}, + self.call('GET', path=['testdocument', guid], reply=['prop1', 'prop2'])) + + def test_properties_OverrideSet(self): + + class TestDocument(db.Resource): + + @db.indexed_property(slot=1, default='1') + def prop(self, value): + return value + + @prop.setter + def prop(self, value): + return '_%s' % value + + @db.blob_property() + def blob1(self, meta): + return meta + + @blob1.setter + def blob1(self, value): + return Blob({'url': file(value['blob']).read()}) + + @db.blob_property() + def blob2(self, meta): + return meta + + @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 + + self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) + guid = self.call('POST', path=['testdocument'], content={}) + + self.assertEqual('_1', self.call('GET', path=['testdocument', guid, 'prop'])) + self.assertRaises(http.NotFound, self.call, 'GET', path=['testdocument', guid, 'blob1']) + + 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']) + + 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']) + + self.call('PUT', path=['testdocument', guid, 'blob1'], content='blob_url') + self.assertEqual('blob_url', self.call('GET', path=['testdocument', guid, 'blob1'])['url']) + + guid = self.call('POST', path=['testdocument'], content={'blob2': 'foo'}) + self.assertEqual(' foo ', file(self.call('GET', path=['testdocument', guid, 'blob2'])['blob']).read()) + + self.call('PUT', path=['testdocument', guid, 'blob2'], content='bar') + self.assertEqual(' bar ', file(self.call('GET', path=['testdocument', guid, 'blob2'])['blob']).read()) + + def test_properties_CallSettersAtTheEnd(self): + + class TestDocument(db.Resource): + + @db.indexed_property(slot=1, typecast=int) + def prop1(self, value): + return value + + @prop1.setter + def prop1(self, value): + return self['prop3'] + value + + @db.indexed_property(slot=2, typecast=int) + def prop2(self, value): + return value + + @prop2.setter + def prop2(self, value): + return self['prop3'] - value + + @db.indexed_property(slot=3, typecast=int) + 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'])) + + def test_properties_PopulateRequiredPropsInSetters(self): + + class TestDocument(db.Resource): + + @db.indexed_property(slot=1, typecast=int) + def prop1(self, value): + return value + + @prop1.setter + def prop1(self, value): + self['prop2'] = value + 1 + return value + + @db.indexed_property(slot=2, typecast=int) + 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 + '!') + + 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()) + + def test_Group(self): + + class TestDocument(db.Resource): + + @db.indexed_property(slot=1) + def prop(self, value): + return value + + self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) + + self.call('POST', path=['testdocument'], content={'prop': 1}) + self.call('POST', path=['testdocument'], content={'prop': 2}) + self.call('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'])) + + def test_CallSetterEvenIfThereIsNoCreatePermissions(self): + + class TestDocument(db.Resource): + + @db.indexed_property(slot=1, acl=ACL.READ, default=0) + def prop(self, value): + return value + + @prop.setter + def prop(self, value): + return value + 1 + + self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) + + self.assertRaises(http.Forbidden, self.call, 'POST', path=['testdocument'], content={'prop': 1}) + + guid = self.call('POST', path=['testdocument'], content={}) + self.assertEqual(1, self.call('GET', path=['testdocument', guid, 'prop'])) + + def test_ReturnDefualtsForMissedProps(self): + + class TestDocument(db.Resource): + + @db.indexed_property(slot=1, default='default') + 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'}) + + self.assertEqual( + [{'prop': 'set'}], + self.call('GET', path=['testdocument'], reply='prop')['result']) + self.assertEqual( + {'prop': 'set'}, + self.call('GET', path=['testdocument', guid], reply='prop')) + self.assertEqual( + 'set', + self.call('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']) + self.assertEqual( + {'prop': 'default'}, + self.call('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'])) + + def test_prop_meta(self): + + class TestDocument(db.Resource): + + @db.indexed_property(slot=1, default='') + def prop(self, value): + return value + + @db.blob_property() + def blob1(self, value): + return value + + @db.blob_property() + def blob2(self, value): + return value + + @blob2.setter + def blob2(self, value): + return {'url': 'http://new', 'foo': 'bar', 'blob_size': 100} + + self.volume = db.Volume(tests.tmpdir, [TestDocument], lambda event: None) + guid = self.call('POST', ['testdocument'], content = {'prop': 'prop', 'blob1': 'blob', 'blob2': ''}) + + assert self.call('HEAD', ['testdocument', guid, 'prop']) is None + meta = self.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') + meta['url'] = 'http://localhost/testdocument/%s/blob1' % guid + 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) + + def test_DefaultAuthor(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]) + + guid = self.call('POST', ['document'], content={}, principal='user') + self.assertEqual( + [{'name': 'user', 'role': 2}], + self.call('GET', ['document', guid, 'author'])) + self.assertEqual( + {'user': {'role': 2, 'order': 0}}, + self.volume['document'].get(guid)['author']) + + self.volume['user'].create({'guid': 'user', 'color': '', 'pubkey': '', 'name': 'User'}) + + guid = self.call('POST', ['document'], content={}, principal='user') + self.assertEqual( + [{'guid': 'user', 'name': 'User', 'role': 3}], + self.call('GET', ['document', guid, 'author'])) + self.assertEqual( + {'user': {'name': 'User', 'role': 3, 'order': 0}}, + self.volume['document'].get(guid)['author']) + + def test_FindByAuthor(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': 'user1', 'color': '', 'pubkey': '', 'name': 'UserName1'}) + self.volume['user'].create({'guid': 'user2', 'color': '', 'pubkey': '', 'name': 'User Name2'}) + self.volume['user'].create({'guid': 'user3', 'color': '', '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') + + self.assertEqual(sorted([ + {'guid': guid1}, + ]), + self.call('GET', ['document'], author='UserName1')['result']) + + self.assertEqual(sorted([ + {'guid': guid1}, + ]), + sorted(self.call('GET', ['document'], query='author:UserName')['result'])) + self.assertEqual(sorted([ + {'guid': guid1}, + {'guid': guid2}, + {'guid': guid3}, + ]), + sorted(self.call('GET', ['document'], query='author:User')['result'])) + self.assertEqual(sorted([ + {'guid': guid2}, + {'guid': guid3}, + ]), + sorted(self.call('GET', ['document'], query='author:Name')['result'])) + + def test_PreserveAuthorsOrder(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': 'user1', 'color': '', 'pubkey': '', 'name': 'User1'}) + self.volume['user'].create({'guid': 'user2', 'color': '', 'pubkey': '', 'name': 'User2'}) + self.volume['user'].create({'guid': 'user3', 'color': '', '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) + + 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'])) + 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']) + + self.call('PUT', ['document', guid], cmd='userdel', user='user2', principal='user1') + self.call('PUT', ['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'])) + 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']) + + self.call('PUT', ['document', guid], cmd='userdel', user='user2', principal='user1') + self.call('PUT', ['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'])) + 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']) + + self.call('PUT', ['document', guid], cmd='userdel', user='user3', principal='user1') + self.call('PUT', ['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'])) + 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_AddUser(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': 'user1', 'color': '', 'pubkey': '', 'name': 'User1'}) + self.volume['user'].create({'guid': 'user2', 'color': '', 'pubkey': '', 'name': 'User2'}) + + guid = self.call('POST', ['document'], content={}, principal='user1') + self.assertEqual([ + {'guid': 'user1', 'name': 'User1', 'role': 3}, + ], + self.call('GET', ['document', guid, 'author'])) + self.assertEqual({ + 'user1': {'name': 'User1', 'role': 3, 'order': 0}, + }, + self.volume['document'].get(guid)['author']) + + self.call('PUT', ['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'])) + self.assertEqual({ + 'user1': {'name': 'User1', 'role': 3, 'order': 0}, + 'user2': {'name': 'User2', 'role': 3, 'order': 1}, + }, + self.volume['document'].get(guid)['author']) + + self.call('PUT', ['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'])) + 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']) + + self.call('PUT', ['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'])) + 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']) + + def test_UpdateAuthor(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': 'user1', 'color': '', 'pubkey': '', 'name': 'User1'}) + guid = self.call('POST', ['document'], content={}, principal='user1') + + self.call('PUT', ['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'])) + self.assertEqual({ + 'user1': {'name': 'User1', 'role': 3, 'order': 0}, + 'User2': {'role': 0, 'order': 1}, + }, + self.volume['document'].get(guid)['author']) + + self.call('PUT', ['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'])) + self.assertEqual({ + 'user1': {'name': 'User1', 'role': 1, 'order': 0}, + 'User2': {'role': 0, 'order': 1}, + }, + self.volume['document'].get(guid)['author']) + + self.call('PUT', ['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'])) + self.assertEqual({ + 'user1': {'name': 'User1', 'role': 1, 'order': 0}, + 'User2': {'role': 2, 'order': 1}, + }, + self.volume['document'].get(guid)['author']) + + def test_DelUser(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': 'user1', 'color': '', 'pubkey': '', 'name': 'User1'}) + self.volume['user'].create({'guid': 'user2', 'color': '', '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') + self.assertEqual([ + {'guid': 'user1', 'name': 'User1', 'role': 3}, + {'guid': 'user2', 'name': 'User2', 'role': 1}, + {'name': 'User3', 'role': 0}, + ], + self.call('GET', ['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']) + + # 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.call('PUT', ['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'])) + self.assertEqual({ + 'user2': {'name': 'User2', 'role': 1, 'order': 1}, + 'User3': {'role': 0, 'order': 2}, + }, + self.volume['document'].get(guid)['author']) + + self.call('PUT', ['document', guid], cmd='userdel', user='User3', principal='user2') + self.assertEqual([ + {'guid': 'user2', 'name': 'User2', 'role': 1}, + ], + self.call('GET', ['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)) + + 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: + request = Request({ + 'REQUEST_METHOD': method, + 'PATH_INFO': '/'.join([''] + path), + 'HTTP_ACCEPT_LANGUAGE': ','.join(accept_language or []), + 'HTTP_HOST': host, + 'wsgi.input': content_stream, + }) + request.cmd = cmd + request.content = content + request.content_type = content_type + if request.content_stream is not None: + request.content_length = len(request.content_stream.getvalue()) + request.update(kwargs) + request.principal = principal + router = Router(routes(self.volume)) + self.response = Response() + return router._call(request, self.response) + + +if __name__ == '__main__': + tests.main() diff --git a/tests/units/db/storage.py b/tests/units/db/storage.py index 149ed03..6eb62e5 100755 --- a/tests/units/db/storage.py +++ b/tests/units/db/storage.py @@ -11,11 +11,10 @@ from os.path import exists from __init__ import tests -from sugar_network.db import env from sugar_network.db.metadata import Metadata, StoredProperty from sugar_network.db.metadata import BlobProperty from sugar_network.db.storage import Storage -from sugar_network.toolkit import BUFFER_SIZE, util +from sugar_network.toolkit import BUFFER_SIZE class StorageTest(tests.Test): diff --git a/tests/units/db/volume.py b/tests/units/db/volume.py deleted file mode 100755 index b968069..0000000 --- a/tests/units/db/volume.py +++ /dev/null @@ -1,1223 +0,0 @@ -#!/usr/bin/env python -# sugar-lint: disable - -import os -import sys -import time -import shutil -import hashlib -from cStringIO import StringIO -from email.message import Message -from email.utils import formatdate -from os.path import dirname, join, abspath, exists - -src_root = abspath(dirname(__file__)) - -from __init__ import tests - -from sugar_network import db, toolkit -from sugar_network.db import env -from sugar_network.db.volume import VolumeCommands -from sugar_network.toolkit import coroutine, http, util - - -class VolumeTest(tests.Test): - - def setUp(self): - tests.Test.setUp(self) - self.response = db.Response() - - def test_PostDefaults(self): - - class Document(db.Document): - - @db.stored_property(default='default') - def w_default(self, value): - return value - - @db.stored_property() - def wo_default(self, value): - return value - - @db.indexed_property(slot=1, default='not_stored_default') - def not_stored_default(self, value): - return value - - self.volume = db.Volume(tests.tmpdir, [Document]) - - self.assertRaises(RuntimeError, self.call, 'POST', document='document', content={}) - - guid = self.call('POST', document='document', content={'wo_default': 'wo_default'}) - self.assertEqual('default', self.call('GET', document='document', guid=guid, prop='w_default')) - self.assertEqual('wo_default', self.call('GET', document='document', guid=guid, prop='wo_default')) - self.assertEqual('not_stored_default', self.call('GET', document='document', guid=guid, prop='not_stored_default')) - - def test_Populate(self): - self.touch( - ('document/1/1/guid', '{"value": "1"}'), - ('document/1/1/ctime', '{"value": 1}'), - ('document/1/1/mtime', '{"value": 1}'), - ('document/1/1/seqno', '{"value": 0}'), - - ('document/2/2/guid', '{"value": "2"}'), - ('document/2/2/ctime', '{"value": 2}'), - ('document/2/2/mtime', '{"value": 2}'), - ('document/2/2/seqno', '{"value": 0}'), - ) - - class Document(db.Document): - pass - - with db.Volume(tests.tmpdir, [Document]) as volume: - for cls in volume.values(): - for __ in cls.populate(): - pass - self.assertEqual( - sorted(['1', '2']), - sorted([i.guid for i in volume['document'].find()[0]])) - - shutil.rmtree('document/index') - - class Document(db.Document): - pass - - with db.Volume(tests.tmpdir, [Document]) as volume: - for cls in volume.values(): - for __ in cls.populate(): - pass - self.assertEqual( - sorted(['1', '2']), - sorted([i.guid for i in volume['document'].find()[0]])) - - def test_Commands(self): - - class TestDocument(db.Document): - - @db.indexed_property(slot=1, default='') - def prop(self, value): - return value - - @db.indexed_property(prefix='L', localized=True, default='') - def localized_prop(self, value): - return value - - self.volume = db.Volume(tests.tmpdir, [TestDocument]) - self.volume['testdocument'].create({'guid': 'guid'}) - - self.assertEqual({ - 'total': 1, - 'result': [ - {'guid': 'guid', 'prop': ''}, - ], - }, - self.call('GET', document='testdocument', reply=['guid', 'prop'])) - - guid_1 = self.call('POST', document='testdocument', content={'prop': 'value_1'}) - assert guid_1 - guid_2 = self.call('POST', document='testdocument', content={'prop': 'value_2'}) - assert guid_2 - - self.assertEqual( - sorted([ - {'guid': 'guid', 'prop': ''}, - {'guid': guid_1, 'prop': 'value_1'}, - {'guid': guid_2, 'prop': 'value_2'}, - ]), - sorted(self.call('GET', document='testdocument', reply=['guid', 'prop'])['result'])) - - self.call('PUT', document='testdocument', guid=guid_1, content={'prop': 'value_3'}) - - self.assertEqual( - sorted([ - {'guid': 'guid', 'prop': ''}, - {'guid': guid_1, 'prop': 'value_3'}, - {'guid': guid_2, 'prop': 'value_2'}, - ]), - sorted(self.call('GET', document='testdocument', reply=['guid', 'prop'])['result'])) - - self.call('DELETE', document='testdocument', guid=guid_2) - - self.assertEqual( - sorted([ - {'guid': 'guid', 'prop': ''}, - {'guid': guid_1, 'prop': 'value_3'}, - ]), - sorted(self.call('GET', document='testdocument', reply=['guid', 'prop'])['result'])) - - self.assertRaises(http.NotFound, self.call, 'GET', document='testdocument', guid=guid_2) - - self.assertEqual( - {'guid': guid_1, 'prop': 'value_3'}, - self.call('GET', document='testdocument', guid=guid_1, reply=['guid', 'prop'])) - - self.assertEqual( - 'value_3', - self.call('GET', document='testdocument', guid=guid_1, prop='prop')) - - def test_SetBLOBs(self): - - class TestDocument(db.Document): - - @db.blob_property() - def blob(self, value): - return value - - self.volume = db.Volume(tests.tmpdir, [TestDocument]) - guid = self.call('POST', document='testdocument', content={}) - - self.call('PUT', document='testdocument', guid=guid, prop='blob', content='blob1') - self.assertEqual('blob1', file(self.call('GET', document='testdocument', guid=guid, prop='blob')['blob']).read()) - - self.call('PUT', document='testdocument', guid=guid, prop='blob', content_stream=StringIO('blob2')) - self.assertEqual('blob2', file(self.call('GET', document='testdocument', guid=guid, prop='blob')['blob']).read()) - - self.call('PUT', document='testdocument', guid=guid, prop='blob', content=None) - self.assertRaises(http.NotFound, self.call, 'GET', document='testdocument', guid=guid, prop='blob') - - def test_SetBLOBsByMeta(self): - - class TestDocument(db.Document): - - @db.blob_property(mime_type='default') - def blob(self, value): - return value - - self.volume = db.Volume(tests.tmpdir, [TestDocument]) - guid = self.call('POST', document='testdocument', content={}) - - self.assertRaises(RuntimeError, self.call, 'PUT', document='testdocument', guid=guid, prop='blob', - content={}, content_type='application/json') - self.assertRaises(http.NotFound, self.call, 'GET', document='testdocument', guid=guid, prop='blob') - - self.touch('file') - self.assertRaises(RuntimeError, self.call, 'PUT', document='testdocument', guid=guid, prop='blob', - content={'blob': 'file'}, content_type='application/json') - self.assertRaises(http.NotFound, self.call, 'GET', document='testdocument', guid=guid, prop='blob') - - self.call('PUT', document='testdocument', guid=guid, prop='blob', - content={'url': 'foo', 'bar': 'probe'}, content_type='application/json') - blob = self.call('GET', document='testdocument', guid=guid, prop='blob') - self.assertEqual('foo', blob['url']) - assert 'bar' not in blob - - def test_RemoveBLOBs(self): - - class TestDocument(db.Document): - - @db.blob_property(mime_type='default') - def blob(self, value): - return value - - self.volume = db.Volume(tests.tmpdir, [TestDocument]) - guid = self.call('POST', document='testdocument', content={'blob': 'blob'}) - - self.assertEqual('blob', file(self.call('GET', document='testdocument', guid=guid, prop='blob')['blob']).read()) - - self.call('PUT', document='testdocument', guid=guid, prop='blob') - self.assertRaises(http.NotFound, self.call, 'GET', document='testdocument', guid=guid, prop='blob') - - def test_RemoveTempBLOBFilesOnFails(self): - - class TestDocument(db.Document): - - @db.blob_property(mime_type='default') - def blob(self, value): - return value - - @blob.setter - def blob(self, value): - raise RuntimeError() - - self.volume = db.Volume(tests.tmpdir, [TestDocument]) - guid = self.call('POST', document='testdocument', content={}) - - self.assertRaises(RuntimeError, self.call, 'PUT', document='testdocument', guid=guid, prop='blob', content='probe') - self.assertEqual(0, len(os.listdir('tmp'))) - - def test_SetBLOBsWithMimeType(self): - - class TestDocument(db.Document): - - @db.blob_property(mime_type='default') - def blob(self, value): - return value - - self.volume = db.Volume(tests.tmpdir, [TestDocument]) - guid = self.call('POST', document='testdocument', content={}) - - self.call('PUT', document='testdocument', guid=guid, prop='blob', content='blob1') - self.assertEqual('default', self.call('GET', document='testdocument', guid=guid, prop='blob')['mime_type']) - self.assertEqual('default', self.response.content_type) - - self.call('PUT', document='testdocument', guid=guid, prop='blob', content='blob1', content_type='foo') - self.assertEqual('foo', self.call('GET', document='testdocument', guid=guid, prop='blob')['mime_type']) - self.assertEqual('foo', self.response.content_type) - - def test_GetBLOBs(self): - - class TestDocument(db.Document): - - @db.blob_property() - def blob(self, value): - return value - - self.volume = db.Volume(tests.tmpdir, [TestDocument]) - guid = self.call('POST', document='testdocument', content={}) - self.call('PUT', document='testdocument', guid=guid, prop='blob', content='blob') - - blob_path = tests.tmpdir + '/testdocument/%s/%s/blob' % (guid[:2], guid) - blob_meta = { - 'seqno': 2, - 'blob': blob_path + '.blob', - 'blob_size': 4, - 'digest': hashlib.sha1('blob').hexdigest(), - 'mime_type': 'application/octet-stream', - 'mtime': int(os.stat(blob_path).st_mtime), - } - - self.assertEqual('blob', file(self.call('GET', document='testdocument', guid=guid, prop='blob')['blob']).read()) - - self.assertEqual( - {'guid': guid, 'blob': blob_meta}, - self.call('GET', document='testdocument', guid=guid, reply=['guid', 'blob'])) - - self.assertEqual([ - {'guid': guid, 'blob': blob_meta}, - ], - self.call('GET', document='testdocument', reply=['guid', 'blob'])['result']) - - def test_GetBLOBsByUrls(self): - - class TestDocument(db.Document): - - @db.blob_property() - def blob(self, value): - return value - - self.volume = db.Volume(tests.tmpdir, [TestDocument]) - guid = self.call('POST', document='testdocument', content={}) - - self.assertRaises(http.NotFound, self.call, 'GET', document='testdocument', guid=guid, prop='blob') - self.assertEqual( - {'blob': 'http://127.0.0.1/testdocument/%s/blob' % guid}, - self.call('GET', document='testdocument', guid=guid, reply=['blob'], static_prefix='http://127.0.0.1')) - self.assertEqual([ - {'blob': 'http://127.0.0.1/testdocument/%s/blob' % guid}, - ], - self.call('GET', document='testdocument', reply=['blob'], static_prefix='http://127.0.0.1')['result']) - - self.call('PUT', document='testdocument', guid=guid, prop='blob', content='file') - self.assertEqual('file', file(self.call('GET', document='testdocument', guid=guid, prop='blob')['blob']).read()) - self.assertEqual( - {'blob': 'http://127.0.0.1/testdocument/%s/blob' % guid}, - self.call('GET', document='testdocument', guid=guid, reply=['blob'], static_prefix='http://127.0.0.1')) - self.assertEqual([ - {'blob': 'http://127.0.0.1/testdocument/%s/blob' % guid}, - ], - self.call('GET', document='testdocument', reply=['blob'], static_prefix='http://127.0.0.1')['result']) - - self.call('PUT', document='testdocument', guid=guid, prop='blob', content={'url': 'http://foo'}, - content_type='application/json') - self.assertEqual('http://foo', self.call('GET', document='testdocument', guid=guid, prop='blob')['url']) - self.assertEqual( - {'blob': 'http://foo'}, - self.call('GET', document='testdocument', guid=guid, reply=['blob'], static_prefix='http://127.0.0.1')) - self.assertEqual([ - {'blob': 'http://foo'}, - ], - self.call('GET', document='testdocument', reply=['blob'], static_prefix='http://127.0.0.1')['result']) - - def test_CommandsGetAbsentBlobs(self): - - class TestDocument(db.Document): - - @db.indexed_property(slot=1, default='') - def prop(self, value): - return value - - @db.blob_property() - def blob(self, value): - return value - - self.volume = db.Volume(tests.tmpdir, [TestDocument]) - - guid = self.call('POST', document='testdocument', content={'prop': 'value'}) - self.assertEqual('value', self.call('GET', document='testdocument', guid=guid, prop='prop')) - self.assertRaises(http.NotFound, self.call, 'GET', document='testdocument', guid=guid, prop='blob') - self.assertEqual( - {'blob': db.PropertyMetadata()}, - self.call('GET', document='testdocument', guid=guid, reply=['blob'])) - - def test_Command_ReplyForGET(self): - - class TestDocument(db.Document): - - @db.indexed_property(slot=1, default='') - def prop(self, value): - return value - - @db.indexed_property(prefix='L', localized=True, default='') - def localized_prop(self, value): - return value - - self.volume = db.Volume(tests.tmpdir, [TestDocument]) - guid = self.call('POST', document='testdocument', content={'prop': 'value'}) - - self.assertEqual( - ['guid', 'prop'], - self.call('GET', document='testdocument', guid=guid, reply=['guid', 'prop']).keys()) - - self.assertEqual( - ['guid'], - self.call('GET', document='testdocument')['result'][0].keys()) - - self.assertEqual( - sorted(['guid', 'prop']), - sorted(self.call('GET', document='testdocument', reply=['prop', 'guid'])['result'][0].keys())) - - self.assertEqual( - sorted(['prop']), - sorted(self.call('GET', document='testdocument', reply=['prop'])['result'][0].keys())) - - def test_DecodeBeforeSetting(self): - - class TestDocument(db.Document): - - @db.indexed_property(slot=1, typecast=int) - def prop(self, value): - return value - - self.volume = db.Volume(tests.tmpdir, [TestDocument]) - - guid = self.call(method='POST', document='testdocument', content={'prop': '-1'}) - self.assertEqual(-1, self.call(method='GET', document='testdocument', guid=guid, prop='prop')) - - def test_LocalizedSet(self): - toolkit._default_lang = 'en' - - class TestDocument(db.Document): - - @db.indexed_property(slot=1, default='') - def prop(self, value): - return value - - @db.indexed_property(prefix='L', localized=True, default='') - def localized_prop(self, value): - return value - - self.volume = db.Volume(tests.tmpdir, [TestDocument]) - directory = self.volume['testdocument'] - - guid = directory.create({'localized_prop': 'value_raw'}) - self.assertEqual({'en': 'value_raw'}, directory.get(guid)['localized_prop']) - self.assertEqual( - [guid], - [i.guid for i in directory.find(0, 100, localized_prop='value_raw')[0]]) - - directory.update(guid, {'localized_prop': 'value_raw2'}) - self.assertEqual({'en': 'value_raw2'}, directory.get(guid)['localized_prop']) - self.assertEqual( - [guid], - [i.guid for i in directory.find(0, 100, localized_prop='value_raw2')[0]]) - - guid = self.call('POST', document='testdocument', accept_language=['ru'], content={'localized_prop': 'value_ru'}) - self.assertEqual({'ru': 'value_ru'}, directory.get(guid)['localized_prop']) - self.assertEqual( - [guid], - [i.guid for i in directory.find(0, 100, localized_prop='value_ru')[0]]) - - self.call('PUT', document='testdocument', guid=guid, accept_language=['en'], content={'localized_prop': 'value_en'}) - self.assertEqual({'ru': 'value_ru', 'en': 'value_en'}, directory.get(guid)['localized_prop']) - self.assertEqual( - [guid], - [i.guid for i in directory.find(0, 100, localized_prop='value_ru')[0]]) - self.assertEqual( - [guid], - [i.guid for i in directory.find(0, 100, localized_prop='value_en')[0]]) - - def test_LocalizedGet(self): - - class TestDocument(db.Document): - - @db.indexed_property(slot=1, default='') - def prop(self, value): - return value - - @db.indexed_property(prefix='L', localized=True, default='') - def localized_prop(self, value): - return value - - self.volume = db.Volume(tests.tmpdir, [TestDocument]) - directory = self.volume['testdocument'] - - guid = self.call('POST', document='testdocument', content={ - 'localized_prop': { - 'ru': 'value_ru', - 'es': 'value_es', - 'en': 'value_en', - }, - }) - - toolkit._default_lang = 'en' - - self.assertEqual( - {'localized_prop': 'value_en'}, - self.call('GET', document='testdocument', guid=guid, reply=['localized_prop'])) - self.assertEqual( - {'localized_prop': 'value_ru'}, - self.call('GET', document='testdocument', guid=guid, accept_language=['ru'], reply=['localized_prop'])) - self.assertEqual( - 'value_ru', - self.call('GET', document='testdocument', guid=guid, accept_language=['ru', 'es'], prop='localized_prop')) - self.assertEqual( - [{'localized_prop': 'value_ru'}], - self.call('GET', document='testdocument', accept_language=['foo', 'ru', 'es'], reply=['localized_prop'])['result']) - - self.assertEqual( - {'localized_prop': 'value_ru'}, - self.call('GET', document='testdocument', guid=guid, accept_language=['ru-RU'], reply=['localized_prop'])) - self.assertEqual( - 'value_ru', - self.call('GET', document='testdocument', guid=guid, accept_language=['ru-RU', 'es'], prop='localized_prop')) - self.assertEqual( - [{'localized_prop': 'value_ru'}], - self.call('GET', document='testdocument', accept_language=['foo', 'ru-RU', 'es'], reply=['localized_prop'])['result']) - - self.assertEqual( - {'localized_prop': 'value_es'}, - self.call('GET', document='testdocument', guid=guid, accept_language=['es'], reply=['localized_prop'])) - self.assertEqual( - 'value_es', - self.call('GET', document='testdocument', guid=guid, accept_language=['es', 'ru'], prop='localized_prop')) - self.assertEqual( - [{'localized_prop': 'value_es'}], - self.call('GET', document='testdocument', accept_language=['foo', 'es', 'ru'], reply=['localized_prop'])['result']) - - self.assertEqual( - {'localized_prop': 'value_en'}, - self.call('GET', document='testdocument', guid=guid, accept_language=['fr'], reply=['localized_prop'])) - self.assertEqual( - 'value_en', - self.call('GET', document='testdocument', guid=guid, accept_language=['fr', 'za'], prop='localized_prop')) - self.assertEqual( - [{'localized_prop': 'value_en'}], - self.call('GET', document='testdocument', accept_language=['foo', 'fr', 'za'], reply=['localized_prop'])['result']) - - toolkit._default_lang = 'foo' - fallback_lang = sorted(['ru', 'es', 'en'])[0] - - self.assertEqual( - {'localized_prop': 'value_%s' % fallback_lang}, - self.call('GET', document='testdocument', guid=guid, accept_language=['fr'], reply=['localized_prop'])) - self.assertEqual( - 'value_%s' % fallback_lang, - self.call('GET', document='testdocument', guid=guid, accept_language=['fr', 'za'], prop='localized_prop')) - self.assertEqual( - [{'localized_prop': 'value_%s' % fallback_lang}], - self.call('GET', document='testdocument', accept_language=['foo', 'fr', 'za'], reply=['localized_prop'])['result']) - - def test_OpenByModuleName(self): - self.touch( - ('foo/bar.py', [ - 'from sugar_network import db', - 'class Bar(db.Document): pass', - ]), - ('foo/__init__.py', ''), - ) - sys.path.insert(0, '.') - - volume = db.Volume('.', ['foo.bar']) - assert exists('bar/index') - volume['bar'].find() - volume.close() - - def test_Command_GetBlobSetByUrl(self): - - class TestDocument(db.Document): - - @db.indexed_property(slot=1, default='') - def prop(self, value): - return value - - @db.blob_property() - def blob(self, value): - return value - - @db.indexed_property(prefix='L', localized=True, default='') - def localized_prop(self, value): - return value - - self.volume = db.Volume(tests.tmpdir, [TestDocument]) - guid = self.call('POST', document='testdocument', content={}) - self.call('PUT', document='testdocument', guid=guid, prop='blob', url='http://sugarlabs.org') - - self.assertEqual( - 'http://sugarlabs.org', - self.call('GET', document='testdocument', guid=guid, prop='blob')['url']) - - def test_on_create(self): - - class TestDocument(db.Document): - - @db.indexed_property(slot=1, default='') - def prop(self, value): - return value - - @db.indexed_property(prefix='L', localized=True, default='') - def localized_prop(self, value): - return value - - self.volume = db.Volume(tests.tmpdir, [TestDocument]) - - ts = int(time.time()) - guid = self.call(method='POST', document='testdocument', content={}) - assert self.volume['testdocument'].get(guid)['ctime'] in range(ts - 1, ts + 1) - assert self.volume['testdocument'].get(guid)['mtime'] in range(ts - 1, ts + 1) - - def test_on_create_Override(self): - - class Commands(VolumeCommands): - - def on_create(self, request, props, event): - props['prop'] = 'overriden' - VolumeCommands.on_create(self, request, props, event) - - class TestDocument(db.Document): - - @db.indexed_property(slot=1, default='') - def prop(self, value): - return value - - @db.indexed_property(prefix='L', localized=True, default='') - def localized_prop(self, value): - return value - - volume = db.Volume(tests.tmpdir, [TestDocument]) - cp = Commands(volume) - - request = db.Request(method='POST', document='testdocument') - request.content = {'prop': 'foo'} - guid = cp.call(request, db.Response()) - self.assertEqual('overriden', volume['testdocument'].get(guid)['prop']) - - request = db.Request(method='PUT', document='testdocument', guid=guid) - request.content = {'prop': 'bar'} - cp.call(request, db.Response()) - self.assertEqual('bar', volume['testdocument'].get(guid)['prop']) - - def test_on_update(self): - - class TestDocument(db.Document): - - @db.indexed_property(slot=1, default='') - def prop(self, value): - return value - - @db.indexed_property(prefix='L', localized=True, default='') - def localized_prop(self, value): - return value - - self.volume = db.Volume(tests.tmpdir, [TestDocument]) - guid = self.call(method='POST', document='testdocument', content={}) - prev_mtime = self.volume['testdocument'].get(guid)['mtime'] - - time.sleep(1) - - self.call(method='PUT', document='testdocument', guid=guid, content={'prop': 'probe'}) - assert self.volume['testdocument'].get(guid)['mtime'] - prev_mtime >= 1 - - def test_on_update_Override(self): - - class Commands(VolumeCommands): - - def on_update(self, request, props, event): - props['prop'] = 'overriden' - VolumeCommands.on_update(self, request, props, event) - - class TestDocument(db.Document): - - @db.indexed_property(slot=1, default='') - def prop(self, value): - return value - - @db.indexed_property(prefix='L', localized=True, default='') - def localized_prop(self, value): - return value - - volume = db.Volume(tests.tmpdir, [TestDocument]) - cp = Commands(volume) - - request = db.Request(method='POST', document='testdocument') - request.content = {'prop': 'foo'} - guid = cp.call(request, db.Response()) - self.assertEqual('foo', volume['testdocument'].get(guid)['prop']) - - request = db.Request(method='PUT', document='testdocument', guid=guid) - request.content = {'prop': 'bar'} - cp.call(request, db.Response()) - self.assertEqual('overriden', volume['testdocument'].get(guid)['prop']) - - def __test_DoNotPassGuidsForCreate(self): - - class TestDocument(db.Document): - - @db.indexed_property(slot=1, default='') - def prop(self, value): - return value - - @db.indexed_property(prefix='L', localized=True, default='') - def localized_prop(self, value): - return value - - self.volume = db.Volume(tests.tmpdir, [TestDocument]) - self.assertRaises(http.Forbidden, self.call, method='POST', document='testdocument', content={'guid': 'foo'}) - guid = self.call(method='POST', document='testdocument', content={}) - assert guid - - def test_seqno(self): - - class Document1(db.Document): - pass - - class Document2(db.Document): - pass - - volume = db.Volume(tests.tmpdir, [Document1, Document2]) - - assert not exists('seqno') - self.assertEqual(0, volume.seqno.value) - - volume['document1'].create({'guid': '1'}) - self.assertEqual(1, volume['document1'].get('1')['seqno']) - volume['document2'].create({'guid': '1'}) - self.assertEqual(2, volume['document2'].get('1')['seqno']) - volume['document1'].create({'guid': '2'}) - self.assertEqual(3, volume['document1'].get('2')['seqno']) - volume['document2'].create({'guid': '2'}) - self.assertEqual(4, volume['document2'].get('2')['seqno']) - - self.assertEqual(4, volume.seqno.value) - assert not exists('seqno') - volume.seqno.commit() - assert exists('seqno') - volume = db.Volume(tests.tmpdir, [Document1, Document2]) - self.assertEqual(4, volume.seqno.value) - - def test_Events(self): - env.index_flush_threshold.value = 0 - env.index_flush_timeout.value = 0 - - class Document1(db.Document): - - @db.indexed_property(slot=1, default='') - def prop(self, value): - pass - - class Document2(db.Document): - - @db.indexed_property(slot=1, default='') - def prop(self, value): - pass - - @db.blob_property() - def blob(self, value): - return value - - self.touch( - ('document1/1/1/guid', '{"value": "1"}'), - ('document1/1/1/ctime', '{"value": 1}'), - ('document1/1/1/mtime', '{"value": 1}'), - ('document1/1/1/prop', '{"value": ""}'), - ('document1/1/1/seqno', '{"value": 0}'), - ) - - events = [] - volume = db.Volume(tests.tmpdir, [Document1, Document2]) - volume.connect(lambda event: events.append(event)) - - volume.populate() - mtime = int(os.stat('document1/index/mtime').st_mtime) - self.assertEqual([ - {'event': 'commit', 'document': 'document1', 'mtime': mtime}, - {'event': 'populate', 'document': 'document1', 'mtime': mtime}, - ], - events) - del events[:] - - volume['document1'].create({'guid': 'guid1'}) - volume['document2'].create({'guid': 'guid2'}) - self.assertEqual([ - {'event': 'create', 'document': 'document1', 'guid': 'guid1'}, - {'event': 'create', 'document': 'document2', 'guid': 'guid2'}, - ], - events) - del events[:] - - volume['document1'].update('guid1', {'prop': 'foo'}) - volume['document2'].update('guid2', {'prop': 'bar'}) - self.assertEqual([ - {'event': 'update', 'document': 'document1', 'guid': 'guid1'}, - {'event': 'update', 'document': 'document2', 'guid': 'guid2'}, - ], - events) - del events[:] - - volume['document1'].delete('guid1') - self.assertEqual([ - {'event': 'delete', 'document': 'document1', 'guid': 'guid1'}, - ], - events) - del events[:] - - volume['document1'].commit() - mtime1 = int(os.stat('document1/index/mtime').st_mtime) - volume['document2'].commit() - mtime2 = int(os.stat('document2/index/mtime').st_mtime) - - self.assertEqual([ - {'event': 'commit', 'document': 'document1', 'mtime': mtime1}, - {'event': 'commit', 'document': 'document2', 'mtime': mtime2}, - ], - events) - - def test_PermissionsNoWrite(self): - - class TestDocument(db.Document): - - @db.indexed_property(slot=1, default='', permissions=db.ACCESS_READ) - def prop(self, value): - pass - - @db.blob_property(permissions=db.ACCESS_READ) - def blob(self, value): - return value - - self.volume = db.Volume(tests.tmpdir, [TestDocument]) - guid = self.call('POST', document='testdocument', content={}) - - self.assertRaises(http.Forbidden, self.call, 'POST', document='testdocument', content={'prop': 'value'}) - self.assertRaises(http.Forbidden, self.call, 'PUT', document='testdocument', guid=guid, content={'prop': 'value'}) - self.assertRaises(http.Forbidden, self.call, 'PUT', document='testdocument', guid=guid, content={'blob': 'value'}) - self.assertRaises(http.Forbidden, self.call, 'PUT', document='testdocument', guid=guid, prop='prop', content='value') - self.assertRaises(http.Forbidden, self.call, 'PUT', document='testdocument', guid=guid, prop='blob', content='value') - - def test_BlobsWritePermissions(self): - - class TestDocument(db.Document): - - @db.blob_property(permissions=db.ACCESS_CREATE | db.ACCESS_WRITE) - def blob1(self, value): - return value - - @db.blob_property(permissions=db.ACCESS_CREATE) - def blob2(self, value): - return value - - self.volume = db.Volume(tests.tmpdir, [TestDocument]) - - guid = self.call('POST', document='testdocument', content={}) - self.call('PUT', document='testdocument', guid=guid, content={'blob1': 'value1', 'blob2': 'value2'}) - self.call('PUT', document='testdocument', guid=guid, content={'blob1': 'value1'}) - self.assertRaises(http.Forbidden, self.call, 'PUT', document='testdocument', guid=guid, content={'blob2': 'value2_'}) - - guid = self.call('POST', document='testdocument', content={}) - self.call('PUT', document='testdocument', guid=guid, prop='blob1', content='value1') - self.call('PUT', document='testdocument', guid=guid, prop='blob2', content='value2') - self.call('PUT', document='testdocument', guid=guid, prop='blob1', content='value1_') - self.assertRaises(http.Forbidden, self.call, 'PUT', document='testdocument', guid=guid, prop='blob2', content='value2_') - - def test_properties_OverrideGet(self): - - class TestDocument(db.Document): - - @db.indexed_property(slot=1, default='1') - def prop1(self, value): - return value - - @db.indexed_property(slot=2, default='2') - def prop2(self, value): - return -1 - - @db.blob_property() - def blob(self, meta): - meta['blob'] = 'new-blob' - return meta - - self.volume = db.Volume(tests.tmpdir, [TestDocument]) - guid = self.call('POST', document='testdocument', content={}) - self.touch(('new-blob', 'new-blob')) - self.call('PUT', document='testdocument', guid=guid, prop='blob', content='old-blob') - - self.assertEqual( - 'new-blob', - self.call('GET', document='testdocument', guid=guid, prop='blob')['blob']) - self.assertEqual( - '1', - self.call('GET', document='testdocument', guid=guid, prop='prop1')) - self.assertEqual( - -1, - self.call('GET', document='testdocument', guid=guid, prop='prop2')) - self.assertEqual( - {'prop1': '1', 'prop2': -1}, - self.call('GET', document='testdocument', guid=guid, reply=['prop1', 'prop2'])) - - def test_properties_OverrideSet(self): - - class TestDocument(db.Document): - - @db.indexed_property(slot=1, default='1') - def prop(self, value): - return value - - @prop.setter - def prop(self, value): - return '_%s' % value - - @db.blob_property() - def blob1(self, meta): - return meta - - @blob1.setter - def blob1(self, value): - return db.PropertyMetadata(url=file(value['blob']).read()) - - @db.blob_property() - def blob2(self, meta): - return meta - - @blob2.setter - def blob2(self, value): - with util.NamedTemporaryFile(delete=False) as f: - f.write(' %s ' % file(value['blob']).read()) - value['blob'] = f.name - return value - - self.volume = db.Volume(tests.tmpdir, [TestDocument]) - guid = self.call('POST', document='testdocument', content={}) - - self.assertEqual('_1', self.call('GET', document='testdocument', guid=guid, prop='prop')) - self.assertRaises(http.NotFound, self.call, 'GET', document='testdocument', guid=guid, prop='blob1') - - self.call('PUT', document='testdocument', guid=guid, prop='prop', content='2') - self.assertEqual('_2', self.call('GET', document='testdocument', guid=guid, prop='prop')) - self.assertRaises(http.NotFound, self.call, 'GET', document='testdocument', guid=guid, prop='blob1') - - self.call('PUT', document='testdocument', guid=guid, content={'prop': 3}) - self.assertEqual('_3', self.call('GET', document='testdocument', guid=guid, prop='prop')) - self.assertRaises(http.NotFound, self.call, 'GET', document='testdocument', guid=guid, prop='blob1') - - self.call('PUT', document='testdocument', guid=guid, prop='blob1', content='blob_url') - self.assertEqual('blob_url', self.call('GET', document='testdocument', guid=guid, prop='blob1')['url']) - - guid = self.call('POST', document='testdocument', content={'blob2': 'foo'}) - self.assertEqual(' foo ', file(self.call('GET', document='testdocument', guid=guid, prop='blob2')['blob']).read()) - - self.call('PUT', document='testdocument', guid=guid, prop='blob2', content='bar') - self.assertEqual(' bar ', file(self.call('GET', document='testdocument', guid=guid, prop='blob2')['blob']).read()) - - def test_properties_CallSettersAtTheEnd(self): - - class TestDocument(db.Document): - - @db.indexed_property(slot=1, typecast=int) - def prop1(self, value): - return value - - @prop1.setter - def prop1(self, value): - return self['prop3'] + value - - @db.indexed_property(slot=2, typecast=int) - def prop2(self, value): - return value - - @prop2.setter - def prop2(self, value): - return self['prop3'] - value - - @db.indexed_property(slot=3, typecast=int) - def prop3(self, value): - return value - - self.volume = db.Volume(tests.tmpdir, [TestDocument]) - guid = self.call('POST', document='testdocument', content={'prop1': 1, 'prop2': 2, 'prop3': 3}) - self.assertEqual(4, self.call('GET', document='testdocument', guid=guid, prop='prop1')) - self.assertEqual(1, self.call('GET', document='testdocument', guid=guid, prop='prop2')) - - def test_properties_PopulateRequiredPropsInSetters(self): - - class TestDocument(db.Document): - - @db.indexed_property(slot=1, typecast=int) - def prop1(self, value): - return value - - @prop1.setter - def prop1(self, value): - self['prop2'] = value + 1 - return value - - @db.indexed_property(slot=2, typecast=int) - 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]) - guid = self.call('POST', document='testdocument', content={'prop1': 1}) - self.assertEqual(1, self.call('GET', document='testdocument', guid=guid, prop='prop1')) - self.assertEqual(2, self.call('GET', document='testdocument', guid=guid, prop='prop2')) - - def test_properties_PopulateRequiredPropsInBlobSetter(self): - - class TestDocument(db.Document): - - @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]) - guid = self.call('POST', document='testdocument', content={'blob': ''}) - self.assertEqual(1, self.call('GET', document='testdocument', guid=guid, prop='prop1')) - self.assertEqual(2, self.call('GET', document='testdocument', guid=guid, prop='prop2')) - - def test_SubCall(self): - - class TestDocument(db.Document): - - @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 util.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', document='testdocument', guid=self.guid, prop='blob', content=value + '!') - - self.volume = db.Volume(tests.tmpdir, [TestDocument]) - - guid = self.call('POST', document='testdocument', content={'blob': '0'}) - coroutine.dispatch() - self.assertEqual('0!', file(self.call('GET', document='testdocument', guid=guid, prop='blob')['blob']).read()) - - self.call('PUT', document='testdocument', guid=guid, prop='blob', content='1') - coroutine.dispatch() - self.assertEqual('0!1!', file(self.call('GET', document='testdocument', guid=guid, prop='blob')['blob']).read()) - - def test_Group(self): - - class TestDocument(db.Document): - - @db.indexed_property(slot=1) - def prop(self, value): - return value - - self.volume = db.Volume(tests.tmpdir, [TestDocument]) - - self.call('POST', document='testdocument', content={'prop': 1}) - self.call('POST', document='testdocument', content={'prop': 2}) - self.call('POST', document='testdocument', content={'prop': 1}) - - self.assertEqual( - sorted([{'prop': 1}, {'prop': 2}]), - sorted(self.call('GET', document='testdocument', reply='prop', group_by='prop')['result'])) - - def test_CallSetterEvenIfThereIsNoCreatePermissions(self): - - class TestDocument(db.Document): - - @db.indexed_property(slot=1, permissions=db.ACCESS_READ, default=0) - def prop(self, value): - return value - - @prop.setter - def prop(self, value): - return value + 1 - - self.volume = db.Volume(tests.tmpdir, [TestDocument]) - - self.assertRaises(http.Forbidden, self.call, 'POST', document='testdocument', content={'prop': 1}) - - guid = self.call('POST', document='testdocument', content={}) - self.assertEqual(1, self.call('GET', document='testdocument', guid=guid, prop='prop')) - - def test_ReturnDefualtsForMissedProps(self): - - class TestDocument(db.Document): - - @db.indexed_property(slot=1, default='default') - def prop(self, value): - return value - - self.volume = db.Volume(tests.tmpdir, [TestDocument]) - guid = self.call('POST', document='testdocument', content={'prop': 'set'}) - - self.assertEqual( - [{'prop': 'set'}], - self.call('GET', document='testdocument', reply='prop')['result']) - self.assertEqual( - {'prop': 'set'}, - self.call('GET', document='testdocument', guid=guid, reply='prop')) - self.assertEqual( - 'set', - self.call('GET', document='testdocument', guid=guid, prop='prop')) - - os.unlink('testdocument/%s/%s/prop' % (guid[:2], guid)) - - self.assertEqual( - [{'prop': 'default'}], - self.call('GET', document='testdocument', reply='prop')['result']) - self.assertEqual( - {'prop': 'default'}, - self.call('GET', document='testdocument', guid=guid, reply='prop')) - self.assertEqual( - 'default', - self.call('GET', document='testdocument', guid=guid, prop='prop')) - - def test_PopulateNonDefualtPropsInSetters(self): - - class TestDocument(db.Document): - - @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]) - - self.assertRaises(RuntimeError, self.call, 'POST', document='testdocument', content={}) - - guid = self.call('POST', document='testdocument', content={'prop2': 'value2'}) - self.assertEqual('value2', self.call('GET', document='testdocument', guid=guid, prop='prop1')) - - def test_prop_meta(self): - - class TestDocument(db.Document): - - @db.indexed_property(slot=1, default='') - def prop(self, value): - return value - - @db.blob_property() - def blob1(self, value): - return value - - @db.blob_property() - def blob2(self, value): - return value - - @blob2.setter - def blob2(self, value): - return {'url': 'http://new', 'foo': 'bar', 'blob_size': 100} - - volume = db.Volume(tests.tmpdir, [TestDocument]) - cp = VolumeCommands(volume) - - request = db.Request(method='POST', document='testdocument') - request.content = {'prop': 'prop', 'blob1': 'blob', 'blob2': ''} - guid = cp.call(request, db.Response()) - - request = db.Request(method='HEAD', document='testdocument', guid=guid, prop='prop') - response = db.Response() - assert cp.call(request, response) is None - meta = volume['testdocument'].get(guid).meta('prop') - meta.pop('value') - self.assertEqual(meta, response.meta) - self.assertEqual(formatdate(meta['mtime'], localtime=False, usegmt=True), response.last_modified) - - request = db.Request(method='HEAD', document='testdocument', guid=guid, prop='blob1') - request.static_prefix = 'http://localhost' - request.path = ['path'] - response = db.Response() - assert cp.call(request, response) is None - meta = volume['testdocument'].get(guid).meta('blob1') - meta.pop('blob') - meta['url'] = 'http://localhost/path' - self.assertEqual(meta, response.meta) - self.assertEqual(len('blob'), response.content_length) - self.assertEqual(formatdate(meta['mtime'], localtime=False, usegmt=True), response.last_modified) - - request = db.Request(method='HEAD', document='testdocument', guid=guid, prop='blob2') - response = db.Response() - assert cp.call(request, response) is None - meta = volume['testdocument'].get(guid).meta('blob2') - self.assertEqual(meta, response.meta) - self.assertEqual(100, response.content_length) - self.assertEqual(formatdate(meta['mtime'], localtime=False, usegmt=True), response.last_modified) - - def call(self, method, document=None, guid=None, prop=None, - accept_language=None, content=None, content_stream=None, - content_type=None, if_modified_since=None, static_prefix=None, - **kwargs): - - class TestRequest(db.Request): - - content_stream = None - content_length = 0 - - request = TestRequest(**kwargs) - request.static_prefix = static_prefix - request.content = content - request.content_stream = content_stream - request.content_type = content_type - request.accept_language = accept_language - request.if_modified_since = if_modified_since - request['method'] = method - if document: - request['document'] = document - if guid: - request['guid'] = guid - if prop: - request['prop'] = prop - if request.content_stream is not None: - request.content_length = len(request.content_stream.getvalue()) - - self.response = db.Response() - cp = VolumeCommands(self.volume) - return cp.call(request, self.response) - - -if __name__ == '__main__': - tests.main() diff --git a/tests/units/resources/__init__.py b/tests/units/model/__init__.py index 345c327..345c327 100644 --- a/tests/units/resources/__init__.py +++ b/tests/units/model/__init__.py diff --git a/tests/units/resources/__main__.py b/tests/units/model/__main__.py index 4444f07..6d8e563 100644 --- a/tests/units/resources/__main__.py +++ b/tests/units/model/__main__.py @@ -7,7 +7,7 @@ from context import * from implementation import * from review import * from solution import * -from volume import * +from routes import * if __name__ == '__main__': tests.main() diff --git a/tests/units/resources/comment.py b/tests/units/model/comment.py index 9c89517..7cf23b9 100755 --- a/tests/units/resources/comment.py +++ b/tests/units/model/comment.py @@ -4,13 +4,13 @@ from __init__ import tests from sugar_network.client import Client -from sugar_network.resources.user import User -from sugar_network.resources.context import Context -from sugar_network.resources.review import Review -from sugar_network.resources.feedback import Feedback -from sugar_network.resources.solution import Solution -from sugar_network.resources.comment import Comment -from sugar_network.resources.implementation import Implementation +from sugar_network.model.user import User +from sugar_network.model.context import Context +from sugar_network.model.review import Review +from sugar_network.model.feedback import Feedback +from sugar_network.model.solution import Solution +from sugar_network.model.comment import Comment +from sugar_network.model.implementation import Implementation from sugar_network.toolkit import http diff --git a/tests/units/resources/context.py b/tests/units/model/context.py index d8c5628..d8c5628 100755 --- a/tests/units/resources/context.py +++ b/tests/units/model/context.py diff --git a/tests/units/resources/implementation.py b/tests/units/model/implementation.py index 176051c..a3afd4b 100755 --- a/tests/units/resources/implementation.py +++ b/tests/units/model/implementation.py @@ -8,64 +8,61 @@ import xapian from __init__ import tests from sugar_network import db -from sugar_network.db.router import Router, route -from sugar_network.resources import implementation -from sugar_network.resources.volume import Volume -from sugar_network.resources.implementation import _encode_version, Implementation -from sugar_network.node.commands import NodeCommands +from sugar_network.model import implementation +from sugar_network.model.implementation import _fmt_version, Implementation from sugar_network.client import IPCClient from sugar_network.toolkit import http, coroutine class ImplementationTest(tests.Test): - def test_encode_version(self): + def test_fmt_version(self): self.assertEqual( xapian.sortable_serialise(eval('1''0000''0000''5''000')), - _encode_version('1')) + _fmt_version('1')) self.assertEqual( xapian.sortable_serialise(eval('1''0002''0000''5''000')), - _encode_version('1.2')) + _fmt_version('1.2')) self.assertEqual( xapian.sortable_serialise(eval('1''0020''0300''5''000')), - _encode_version('1.20.300')) + _fmt_version('1.20.300')) self.assertEqual( xapian.sortable_serialise(eval('1''0020''0300''5''000')), - _encode_version('1.20.300.4444')) + _fmt_version('1.20.300.4444')) self.assertEqual( xapian.sortable_serialise(eval('1''9999''0000''5''000')), - _encode_version('10001.99999.10000')) + _fmt_version('10001.99999.10000')) self.assertEqual( xapian.sortable_serialise(eval('1''0000''0000''3''000')), - _encode_version('1-pre')) + _fmt_version('1-pre')) self.assertEqual( xapian.sortable_serialise(eval('1''0000''0000''4''000')), - _encode_version('1-rc')) + _fmt_version('1-rc')) self.assertEqual( xapian.sortable_serialise(eval('1''0000''0000''5''000')), - _encode_version('1-')) + _fmt_version('1-')) self.assertEqual( xapian.sortable_serialise(eval('1''0000''0000''6''000')), - _encode_version('1-r')) + _fmt_version('1-r')) self.assertEqual( xapian.sortable_serialise(eval('1''0000''0000''3''001')), - _encode_version('1-pre1')) + _fmt_version('1-pre1')) self.assertEqual( xapian.sortable_serialise(eval('1''0000''0000''4''002')), - _encode_version('1-rc2')) + _fmt_version('1-rc2')) self.assertEqual( xapian.sortable_serialise(eval('1''0000''0000''6''003')), - _encode_version('1-r3')) + _fmt_version('1-r3')) self.assertEqual( xapian.sortable_serialise(eval('1''0000''0000''6''000')), - _encode_version('1-r-2-3')) + _fmt_version('1-r-2-3')) self.assertEqual( xapian.sortable_serialise(eval('1''0000''0000''6''001')), - _encode_version('1-r1.2-3')) + _fmt_version('1-r1.2-3')) def test_WrongAuthor(self): self.start_online_client() diff --git a/tests/units/resources/review.py b/tests/units/model/review.py index c5cd30a..0e50f5a 100755 --- a/tests/units/resources/review.py +++ b/tests/units/model/review.py @@ -4,11 +4,11 @@ from __init__ import tests from sugar_network.client import Client -from sugar_network.resources.user import User -from sugar_network.resources.context import Context -from sugar_network.resources.review import Review -from sugar_network.resources.artifact import Artifact -from sugar_network.resources.implementation import Implementation +from sugar_network.model.user import User +from sugar_network.model.context import Context +from sugar_network.model.review import Review +from sugar_network.model.artifact import Artifact +from sugar_network.model.implementation import Implementation class ReviewTest(tests.Test): diff --git a/tests/units/model/routes.py b/tests/units/model/routes.py new file mode 100755 index 0000000..dd5bcb3 --- /dev/null +++ b/tests/units/model/routes.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python +# sugar-lint: disable + +import os +import json +import time +from email.utils import formatdate +from os.path import exists + +from __init__ import tests, src_root + +from sugar_network import db, model +from sugar_network.model.user import User +from sugar_network.toolkit.router import Router +from sugar_network.toolkit import coroutine + + +class RoutesTest(tests.Test): + + def test_StaticFiles(self): + router = Router(model.Routes()) + 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): + + @db.indexed_property(slot=1) + def prop(self, value): + return value + + routes = model.Routes() + volume = db.Volume('db', [Document], routes.broadcast) + events = [] + + def read_events(): + for event in routes.subscribe(event='!commit'): + if not event.strip(): + continue + assert event.startswith('data: ') + assert event.endswith('\n\n') + event = json.loads(event[6:]) + events.append(event) + + job = coroutine.spawn(read_events) + coroutine.dispatch() + volume['document'].create({'guid': 'guid', 'prop': 'value1'}) + coroutine.dispatch() + volume['document'].update('guid', {'prop': 'value2'}) + coroutine.dispatch() + volume['document'].delete('guid') + coroutine.dispatch() + volume['document'].commit() + coroutine.sleep(.5) + job.kill() + + self.assertEqual([ + {'guid': 'guid', 'resource': 'document', 'event': 'create'}, + {'guid': 'guid', 'resource': 'document', 'event': 'update'}, + {'guid': 'guid', 'event': 'delete', 'resource': u'document'}, + ], + events) + + def test_SubscribeWithPong(self): + routes = model.Routes() + for event in routes.subscribe(ping=True): + break + self.assertEqual('data: {"event": "pong"}\n\n', event) + + + +if __name__ == '__main__': + tests.main() diff --git a/tests/units/resources/solution.py b/tests/units/model/solution.py index a56ccfa..dafb5a7 100755 --- a/tests/units/resources/solution.py +++ b/tests/units/model/solution.py @@ -4,11 +4,11 @@ from __init__ import tests from sugar_network.client import Client -from sugar_network.resources.user import User -from sugar_network.resources.context import Context -from sugar_network.resources.feedback import Feedback -from sugar_network.resources.solution import Solution -from sugar_network.resources.implementation import Implementation +from sugar_network.model.user import User +from sugar_network.model.context import Context +from sugar_network.model.feedback import Feedback +from sugar_network.model.solution import Solution +from sugar_network.model.implementation import Implementation class SolutionTest(tests.Test): diff --git a/tests/units/node/__main__.py b/tests/units/node/__main__.py index 4ab7386..ac37315 100644 --- a/tests/units/node/__main__.py +++ b/tests/units/node/__main__.py @@ -2,7 +2,6 @@ from __init__ import tests -from auth import * from downloads import * from files import * from master import * diff --git a/tests/units/node/auth.py b/tests/units/node/auth.py deleted file mode 100755 index f445f3c..0000000 --- a/tests/units/node/auth.py +++ /dev/null @@ -1,163 +0,0 @@ -#!/usr/bin/env python -# sugar-lint: disable - -import os -import json - -from __init__ import tests - -from sugar_network import db, client -from sugar_network.node import auth -from sugar_network.client import IPCClient, Client -from sugar_network.resources.user import User -from sugar_network.toolkit import http, enforce - - -class AuthTest(tests.Test): - - def test_Config(self): - self.touch(('authorization.conf', [ - '[user_1]', - 'role_1 = True', - '[user_2]', - 'role_2 = False', - ])) - - request = db.Request() - request.principal = 'user_1' - self.assertEqual(True, auth.try_validate(request, 'role_1')) - auth.validate(request, 'role_1') - - request.principal = 'user_2' - self.assertEqual(False, auth.try_validate(request, 'role_2')) - self.assertRaises(http.Forbidden, auth.validate, request, 'role_2') - - request.principal = 'user_3' - self.assertEqual(False, auth.try_validate(request, 'role_1')) - self.assertEqual(False, auth.try_validate(request, 'role_2')) - self.assertRaises(http.Forbidden, auth.validate, request, 'role_1') - self.assertRaises(http.Forbidden, auth.validate, request, 'role_2') - - def test_FullWriteForRoot(self): - conn = Client() - - self.start_master() - conn.post(['context'], { - 'guid': 'guid', - 'type': 'package', - 'title': 'title', - 'summary': 'summary', - 'description': 'description', - }) - self.assertNotEqual('probe', conn.get(['context', 'guid', 'title'])) - self.stop_nodes() - - self.touch(( - 'master/context/gu/guid/author', - json.dumps({"seqno": 1, "value": {"fake": {"role": 3}}}), - )) - - self.start_master() - self.assertRaises(http.Forbidden, conn.put, ['context', 'guid'], {'title': 'probe'}) - - self.touch(('authorization.conf', [ - '[%s]' % tests.UID, - 'root = True', - ])) - auth.reset() - conn.put(['context', 'guid'], {'title': 'probe'}) - self.assertEqual('probe', conn.get(['context', 'guid', 'title'])) - - def test_Anonymous(self): - conn = http.Client(client.api_url.value) - - props = {'guid': 'guid', - 'type': 'package', - 'title': 'title', - 'summary': 'summary', - 'description': 'description', - } - self.start_master() - - self.assertRaises(RuntimeError, conn.post, ['context'], props) - - self.touch(('authorization.conf', [ - '[anonymous]', - 'user = True', - ])) - auth.reset() - conn.post(['context'], props) - self.assertEqual('title', conn.get(['context', 'guid', 'title'])) - self.assertEqual([], conn.get(['context', 'guid', 'author'])) - - self.stop_nodes() - self.touch(( - 'master/context/gu/guid/author', - json.dumps({"seqno": 1, "value": {"fake": {"role": 3}}}), - )) - self.start_master() - - auth.reset() - self.assertRaises(http.Forbidden, conn.put, ['context', 'guid'], {'title': 'probe'}) - - self.touch(('authorization.conf', [ - '[anonymous]', - 'user = True', - 'root = True', - ])) - auth.reset() - conn.put(['context', 'guid'], {'title': 'probe'}) - self.assertEqual('probe', conn.get(['context', 'guid', 'title'])) - self.assertEqual([{'name': 'fake', 'role': 3}], conn.get(['context', 'guid', 'author'])) - - def test_LiveUpdate(self): - conn = http.Client(client.api_url.value) - - props = {'guid': 'guid', - 'type': 'package', - 'title': 'title', - 'summary': 'summary', - 'description': 'description', - } - self.start_master() - - self.touch(('authorization.conf', '')) - os.utime('authorization.conf', (1, 1)) - self.assertRaises(RuntimeError, conn.post, ['context'], props) - - self.touch(('authorization.conf', [ - '[anonymous]', - 'user = True', - ])) - os.utime('authorization.conf', (2, 2)) - conn.post(['context'], props) - self.assertEqual([], conn.get(['context', 'guid', 'author'])) - - self.touch(('authorization.conf', '')) - os.utime('authorization.conf', (3, 3)) - self.assertRaises(RuntimeError, conn.post, ['context'], props) - - def test_DefaultAuthorization(self): - - class Document(db.Document): - - @db.document_command(method='GET', cmd='probe1', - mime_type='application/json') - def probe1(cls, directory): - return 'ok1' - - @db.document_command(method='GET', cmd='probe2', - permissions=db.ACCESS_AUTH, mime_type='application/json') - def probe2(cls, directory): - return 'ok2' - - self.start_master([User, Document]) - conn = Client() - - guid = conn.post(['document'], {}) - self.assertEqual('ok1', conn.get(['document', guid], cmd='probe1')) - self.assertEqual('ok2', conn.get(['document', guid], cmd='probe2')) - - -if __name__ == '__main__': - tests.main() diff --git a/tests/units/node/files.py b/tests/units/node/files.py index 2676aa9..111b7a8 100755 --- a/tests/units/node/files.py +++ b/tests/units/node/files.py @@ -11,7 +11,6 @@ from cStringIO import StringIO from __init__ import tests from sugar_network import db, toolkit -from sugar_network.toolkit import util from sugar_network.node import files @@ -27,14 +26,14 @@ class FilesTest(tests.Test): return str(self.uuid) def test_Index_Populate(self): - seqno = util.Seqno('seqno') + seqno = toolkit.Seqno('seqno') seeder = files.Index('files', 'index', seqno) os.utime('files', (1, 1)) assert seeder.sync() assert not seeder.sync() - in_seq = util.Sequence([[1, None]]) + in_seq = toolkit.Sequence([[1, None]]) self.assertEqual( [{'op': 'commit', 'sequence': []}], [i for i in seeder.diff(in_seq)]) @@ -48,7 +47,7 @@ class FilesTest(tests.Test): os.utime('files', (1, 1)) assert not seeder.sync() - in_seq = util.Sequence([[1, None]]) + in_seq = toolkit.Sequence([[1, None]]) self.assertEqual( [{'op': 'commit', 'sequence': []}], [i for i in seeder.diff(in_seq)]) @@ -59,7 +58,7 @@ class FilesTest(tests.Test): os.utime('files', (2, 2)) assert seeder.sync() - in_seq = util.Sequence([[1, None]]) + in_seq = toolkit.Sequence([[1, None]]) self.assertEqual(sorted([ {'op': 'commit', 'sequence': [[1, 3]]}, {'op': 'update', 'blob_size': 1, 'blob': '1', 'path': '1'}, @@ -79,14 +78,14 @@ class FilesTest(tests.Test): json.load(file('index'))) assert not seeder.sync() - in_seq = util.Sequence([[4, None]]) + in_seq = toolkit.Sequence([[4, None]]) self.assertEqual( [{'op': 'commit', 'sequence': []}], [i for i in seeder.diff(in_seq)]) self.assertEqual(3, seqno.value) def test_Index_SelectiveDiff(self): - seqno = util.Seqno('seqno') + seqno = toolkit.Seqno('seqno') seeder = files.Index('files', 'index', seqno) self.touch(('files/1', '1')) @@ -96,7 +95,7 @@ class FilesTest(tests.Test): self.touch(('files/5', '5')) self.utime('files', 1) - in_seq = util.Sequence([[2, 2], [4, 10], [20, None]]) + in_seq = toolkit.Sequence([[2, 2], [4, 10], [20, None]]) self.assertEqual(sorted([ {'op': 'commit', 'sequence': [[2, 5]]}, {'op': 'update', 'blob_size': 1, 'blob': '2', 'path': '2'}, @@ -106,7 +105,7 @@ class FilesTest(tests.Test): sorted(files_diff(seeder, in_seq))) def test_Index_PartialDiff(self): - seqno = util.Seqno('seqno') + seqno = toolkit.Seqno('seqno') seeder = files.Index('files', 'index', seqno) self.touch(('files/1', '1')) @@ -114,7 +113,7 @@ class FilesTest(tests.Test): self.touch(('files/3', '3')) self.utime('files', 1) - in_seq = util.Sequence([[1, None]]) + in_seq = toolkit.Sequence([[1, None]]) diff = seeder.diff(in_seq) record = next(diff) record['blob'] = ''.join([i for i in record['blob']]) @@ -133,7 +132,7 @@ class FilesTest(tests.Test): self.assertRaises(StopIteration, diff.next) def test_Index_diff_Stretch(self): - seqno = util.Seqno('seqno') + seqno = toolkit.Seqno('seqno') seeder = files.Index('files', 'index', seqno) self.touch(('files/1', '1')) @@ -141,7 +140,7 @@ class FilesTest(tests.Test): self.touch(('files/3', '3')) self.utime('files', 1) - in_seq = util.Sequence([[1, 1], [3, None]]) + in_seq = toolkit.Sequence([[1, 1], [3, None]]) diff = seeder.diff(in_seq) record = next(diff) record['blob'] = ''.join([i for i in record['blob']]) @@ -153,7 +152,7 @@ class FilesTest(tests.Test): self.assertRaises(StopIteration, diff.next) def test_Index_diff_DoNotStretchContinuesPacket(self): - seqno = util.Seqno('seqno') + seqno = toolkit.Seqno('seqno') seeder = files.Index('files', 'index', seqno) self.touch(('files/1', '1')) @@ -161,8 +160,8 @@ class FilesTest(tests.Test): self.touch(('files/3', '3')) self.utime('files', 1) - in_seq = util.Sequence([[1, 1], [3, None]]) - diff = seeder.diff(in_seq, util.Sequence([[1, 1]])) + in_seq = toolkit.Sequence([[1, 1], [3, None]]) + diff = seeder.diff(in_seq, toolkit.Sequence([[1, 1]])) record = next(diff) record['blob'] = ''.join([i for i in record['blob']]) self.assertEqual({'op': 'update', 'blob_size': 1, 'blob': '1', 'path': '1'}, record) @@ -173,7 +172,7 @@ class FilesTest(tests.Test): self.assertRaises(StopIteration, diff.next) def test_Index_DiffUpdatedFiles(self): - seqno = util.Seqno('seqno') + seqno = toolkit.Seqno('seqno') seeder = files.Index('files', 'index', seqno) self.touch(('files/1', '1')) @@ -182,7 +181,7 @@ class FilesTest(tests.Test): self.utime('files', 1) os.utime('files', (1, 1)) - for __ in seeder.diff(util.Sequence([[1, None]])): + for __ in seeder.diff(toolkit.Sequence([[1, None]])): pass self.assertEqual(3, seqno.value) @@ -190,7 +189,7 @@ class FilesTest(tests.Test): self.assertEqual( [{'op': 'commit', 'sequence': []}], - [i for i in seeder.diff(util.Sequence([[4, None]]))]) + [i for i in seeder.diff(toolkit.Sequence([[4, None]]))]) self.assertEqual(3, seqno.value) os.utime('files', (3, 3)) @@ -199,7 +198,7 @@ class FilesTest(tests.Test): {'op': 'update', 'blob_size': 1, 'blob': '2', 'path': '2'}, {'op': 'commit', 'sequence': [[4, 4]]}, ]), - sorted(files_diff(seeder, util.Sequence([[4, None]])))) + sorted(files_diff(seeder, toolkit.Sequence([[4, None]])))) self.assertEqual(4, seqno.value) os.utime('files/1', (4, 4)) @@ -211,7 +210,7 @@ class FilesTest(tests.Test): {'op': 'update', 'blob_size': 1, 'blob': '3', 'path': '3'}, {'op': 'commit', 'sequence': [[5, 6]]}, ]), - sorted(files_diff(seeder, util.Sequence([[5, None]])))) + sorted(files_diff(seeder, toolkit.Sequence([[5, None]])))) self.assertEqual(6, seqno.value) self.assertEqual(sorted([ @@ -220,11 +219,11 @@ class FilesTest(tests.Test): {'op': 'update', 'blob_size': 1, 'blob': '3', 'path': '3'}, {'op': 'commit', 'sequence': [[1, 6]]}, ]), - sorted(files_diff(seeder, util.Sequence([[1, None]])))) + sorted(files_diff(seeder, toolkit.Sequence([[1, None]])))) self.assertEqual(6, seqno.value) def test_Index_DiffCreatedFiles(self): - seqno = util.Seqno('seqno') + seqno = toolkit.Seqno('seqno') seeder = files.Index('files', 'index', seqno) self.touch(('files/1', '1')) @@ -233,7 +232,7 @@ class FilesTest(tests.Test): self.utime('files', 1) os.utime('files', (1, 1)) - for __ in seeder.diff(util.Sequence([[1, None]])): + for __ in seeder.diff(toolkit.Sequence([[1, None]])): pass self.assertEqual(3, seqno.value) @@ -243,7 +242,7 @@ class FilesTest(tests.Test): self.assertEqual( [{'op': 'commit', 'sequence': []}], - [i for i in seeder.diff(util.Sequence([[4, None]]))]) + [i for i in seeder.diff(toolkit.Sequence([[4, None]]))]) self.assertEqual(3, seqno.value) os.utime('files/4', (2, 2)) @@ -253,7 +252,7 @@ class FilesTest(tests.Test): {'op': 'update', 'blob_size': 1, 'blob': '4', 'path': '4'}, {'op': 'commit', 'sequence': [[4, 4]]}, ]), - sorted(files_diff(seeder, util.Sequence([[4, None]])))) + sorted(files_diff(seeder, toolkit.Sequence([[4, None]])))) self.assertEqual(4, seqno.value) self.touch(('files/5', '5')) @@ -267,11 +266,11 @@ class FilesTest(tests.Test): {'op': 'update', 'blob_size': 1, 'blob': '6', 'path': '6'}, {'op': 'commit', 'sequence': [[5, 6]]}, ]), - sorted(files_diff(seeder, util.Sequence([[5, None]])))) + sorted(files_diff(seeder, toolkit.Sequence([[5, None]])))) self.assertEqual(6, seqno.value) def test_Index_DiffDeletedFiles(self): - seqno = util.Seqno('seqno') + seqno = toolkit.Seqno('seqno') seeder = files.Index('files', 'index', seqno) self.touch(('files/1', '1')) @@ -280,7 +279,7 @@ class FilesTest(tests.Test): self.utime('files', 1) os.utime('files', (1, 1)) - for __ in seeder.diff(util.Sequence([[1, None]])): + for __ in seeder.diff(toolkit.Sequence([[1, None]])): pass self.assertEqual(3, seqno.value) @@ -294,7 +293,7 @@ class FilesTest(tests.Test): {'op': 'delete', 'path': '2'}, {'op': 'commit', 'sequence': [[1, 4]]}, ]), - sorted(files_diff(seeder, util.Sequence([[1, None]])))) + sorted(files_diff(seeder, toolkit.Sequence([[1, None]])))) self.assertEqual(4, seqno.value) os.unlink('files/1') @@ -308,7 +307,7 @@ class FilesTest(tests.Test): {'op': 'delete', 'path': '3'}, {'op': 'commit', 'sequence': [[1, 6]]}, ]), - sorted([i for i in seeder.diff(util.Sequence([[1, None]]))])) + sorted([i for i in seeder.diff(toolkit.Sequence([[1, None]]))])) self.assertEqual(6, seqno.value) assert not seeder.sync() @@ -318,7 +317,7 @@ class FilesTest(tests.Test): {'op': 'delete', 'path': '3'}, {'op': 'commit', 'sequence': [[1, 6]]}, ]), - sorted([i for i in seeder.diff(util.Sequence([[1, None]]))])) + sorted([i for i in seeder.diff(toolkit.Sequence([[1, None]]))])) self.assertEqual(6, seqno.value) def test_merge_Updated(self): diff --git a/tests/units/node/master.py b/tests/units/node/master.py index 242e159..f5dbc1b 100755 --- a/tests/units/node/master.py +++ b/tests/units/node/master.py @@ -153,7 +153,7 @@ class MasterTest(tests.Test): events = [] def read_events(): for event in ipc.subscribe(): - if event.get('document') == 'implementation': + if event.get('resource') == 'implementation': events.append(event) job = coroutine.spawn(read_events) @@ -174,7 +174,7 @@ class MasterTest(tests.Test): }) coroutine.sleep(.5) self.assertEqual([ - {'event': 'populate', 'document': 'implementation', 'mtime': int(os.stat('master/implementation/index/mtime').st_mtime)}, + {'event': 'populate', 'resource': 'implementation', 'mtime': int(os.stat('master/implementation/index/mtime').st_mtime)}, ], events) self.assertEqual({ @@ -189,7 +189,7 @@ class MasterTest(tests.Test): events = [] def read_events(): for event in ipc.subscribe(): - if event.get('document') == 'implementation': + if event.get('resource') == 'implementation': events.append(event) job = coroutine.spawn(read_events) @@ -205,7 +205,7 @@ class MasterTest(tests.Test): ipc.put(['context', guid, 'dependencies'], ['foo']) coroutine.sleep(.1) self.assertEqual([ - {'event': 'populate', 'document': 'implementation', 'mtime': int(os.stat('master/implementation/index/mtime').st_mtime)}, + {'event': 'populate', 'resource': 'implementation', 'mtime': int(os.stat('master/implementation/index/mtime').st_mtime)}, ], events) diff --git a/tests/units/node/node.py b/tests/units/node/node.py index a1bb46b..85dcff3 100755 --- a/tests/units/node/node.py +++ b/tests/units/node/node.py @@ -10,22 +10,22 @@ from os.path import exists from __init__ import tests -from sugar_network import db, node +from sugar_network import db, node, model from sugar_network.client import Client 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.commands import NodeCommands -from sugar_network.node.master import MasterCommands -from sugar_network.resources.volume import Volume, Resource -from sugar_network.resources.user import User -from sugar_network.resources.context import Context -from sugar_network.resources.implementation import Implementation -from sugar_network.resources.review import Review -from sugar_network.resources.feedback import Feedback -from sugar_network.resources.artifact import Artifact -from sugar_network.resources.solution import Solution -from sugar_network.resources.user import 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.implementation import Implementation +from sugar_network.model.review import Review +from sugar_network.model.feedback import Feedback +from sugar_network.model.artifact import Artifact +from sugar_network.model.solution import Solution +from sugar_network.model.user import User +from sugar_network.toolkit.router import Router, Request, Response, fallbackroute, Blob, ACL, route class NodeTest(tests.Test): @@ -37,8 +37,8 @@ class NodeTest(tests.Test): stats_user.stats_user_rras.value = ['RRA:AVERAGE:0.5:1:100'] def test_UserStats(self): - volume = Volume('db') - cp = NodeCommands('guid', volume) + volume = db.Volume('db', model.RESOURCES) + cp = NodeRoutes('guid', volume) call(cp, method='POST', document='user', principal=tests.UID, content={ 'name': 'user', @@ -110,8 +110,8 @@ class NodeTest(tests.Test): for i in range(100): rrd['user'].put({'total': i}, ts + i) - volume = Volume('db', [User, Context, Review, Feedback, Solution, Artifact]) - cp = NodeCommands('guid', volume) + volume = db.Volume('db', model.RESOURCES) + cp = NodeRoutes('guid', volume) self.assertEqual({ 'user': [ @@ -134,10 +134,11 @@ class NodeTest(tests.Test): cp.stats(ts, ts + 12, 3, ['user.total'])) def test_HandleDeletes(self): - volume = Volume('db') - cp = NodeCommands('guid', volume) + volume = db.Volume('db', model.RESOURCES) + volume['user'].create({'guid': tests.UID, 'name': 'user', 'color': '', 'pubkey': tests.PUBKEY}) + cp = NodeRoutes('guid', volume) - guid = call(cp, method='POST', document='context', principal='principal', content={ + guid = call(cp, method='POST', document='context', principal=tests.UID, content={ 'type': 'activity', 'title': 'title', 'summary': 'summary', @@ -154,43 +155,44 @@ class NodeTest(tests.Test): call(cp, method='GET', document='context', guid=guid, reply=['guid', 'title', 'layer'])) self.assertEqual(['public'], volume['context'].get(guid)['layer']) + def subscribe(): + for event in cp.subscribe(): + events.append(json.loads(event[6:])) events = [] - volume.connect(lambda event: events.append(event)) - call(cp, method='DELETE', document='context', guid=guid, principal='principal') + coroutine.spawn(subscribe) coroutine.dispatch() + call(cp, method='DELETE', document='context', guid=guid, principal=tests.UID) + 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', 'document': 'context', 'guid': guid}, - {'event': 'commit', 'document': 'context', 'mtime': int(os.stat('db/context/index/mtime').st_mtime)}, - ], - events) + self.assertEqual({'event': 'delete', 'resource': 'context', 'guid': guid}, events[0]) def test_SimulateDeleteEvents(self): - volume = Volume('db') - cp = NodeCommands('guid', volume) + volume = db.Volume('db', model.RESOURCES) + volume['user'].create({'guid': tests.UID, 'name': 'user', 'color': '', 'pubkey': tests.PUBKEY}) + cp = NodeRoutes('guid', volume) - guid = call(cp, method='POST', document='context', principal='principal', content={ + guid = call(cp, method='POST', document='context', principal=tests.UID, content={ 'type': 'activity', 'title': 'title', 'summary': 'summary', 'description': 'description', }) + def subscribe(): + for event in cp.subscribe(): + events.append(json.loads(event[6:])) events = [] - volume.connect(lambda event: events.append(event)) - call(cp, method='PUT', document='context', guid=guid, principal='principal', content={'layer': ['deleted']}) + coroutine.spawn(subscribe) coroutine.dispatch() - self.assertEqual([ - {'event': 'delete', 'document': 'context', 'guid': guid}, - {'event': 'commit', 'document': 'context', 'mtime': int(os.stat('db/context/index/mtime').st_mtime)}, - ], - events) + 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 test_RegisterUser(self): - cp = NodeCommands('guid', Volume('db', [User])) + cp = NodeRoutes('guid', db.Volume('db', [User])) guid = call(cp, method='POST', document='user', principal='fake', content={ 'name': 'user', @@ -203,50 +205,57 @@ class NodeTest(tests.Test): self.assertEqual('user', call(cp, method='GET', document='user', guid=tests.UID, prop='name')) def test_UnauthorizedCommands(self): + volume = db.Volume('db', model.RESOURCES) + volume['user'].create({'guid': tests.UID, 'name': 'user', 'color': '', 'pubkey': tests.PUBKEY}) - class Document(Resource): + class Routes(NodeRoutes): - @db.document_command(method='GET', cmd='probe1', - permissions=db.ACCESS_AUTH) + @route('GET', [None, None], cmd='probe1', acl=ACL.AUTH) def probe1(self, directory): pass - @db.document_command(method='GET', cmd='probe2') + @route('GET', [None, None], cmd='probe2') def probe2(self, directory): pass - cp = NodeCommands('guid', Volume('db', [User, Document])) - guid = call(cp, method='POST', document='document', principal='user', content={}) + class Document(db.Resource): + pass + + cp = Routes('guid', 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) - call(cp, method='GET', cmd='probe1', document='document', guid=guid, principal='user') + call(cp, method='GET', cmd='probe1', document='document', guid=guid, principal=tests.UID) call(cp, method='GET', cmd='probe2', document='document', guid=guid) def test_ForbiddenCommands(self): - class Document(Resource): + class Routes(NodeRoutes): - @db.document_command(method='GET', cmd='probe1', - permissions=db.ACCESS_AUTHOR) + @route('GET', [None, None], cmd='probe1', acl=ACL.AUTHOR) def probe1(self): pass - @db.document_command(method='GET', cmd='probe2') + @route('GET', [None, None], cmd='probe2') def probe2(self): pass - class User(db.Document): + class Document(db.Resource): pass - cp = NodeCommands('guid', Volume('db', [User, Document])) - guid = call(cp, method='POST', document='document', principal='principal', content={}) + volume = db.Volume('db', [User, Document]) + volume['user'].create({'guid': tests.UID, 'name': 'user', 'color': '', 'pubkey': tests.PUBKEY}) + cp = Routes('guid', volume) + + guid = call(cp, method='POST', document='document', principal=tests.UID, content={}) self.assertRaises(http.Forbidden, call, cp, method='GET', cmd='probe1', document='document', guid=guid) - self.assertRaises(http.Forbidden, call, cp, method='GET', cmd='probe1', document='document', guid=guid, principal='fake') - call(cp, method='GET', cmd='probe1', document='document', guid=guid, principal='principal') + self.assertRaises(http.Unauthorized, call, cp, method='GET', cmd='probe1', document='document', guid=guid, principal='fake') + call(cp, method='GET', cmd='probe1', document='document', guid=guid, principal=tests.UID) call(cp, method='GET', cmd='probe2', document='document', guid=guid) def test_ForbiddenCommandsForUserResource(self): - cp = NodeCommands('guid', Volume('db', [User])) + cp = NodeRoutes('guid', db.Volume('db', [User])) call(cp, method='POST', document='user', principal='fake', content={ 'name': 'user1', @@ -258,39 +267,134 @@ class NodeTest(tests.Test): self.assertEqual('user1', call(cp, method='GET', document='user', guid=tests.UID, prop='name')) self.assertRaises(http.Unauthorized, call, cp, method='PUT', document='user', guid=tests.UID, content={'name': 'user2'}) - self.assertRaises(http.Forbidden, call, cp, method='PUT', document='user', guid=tests.UID, principal='fake', content={'name': 'user2'}) + self.assertRaises(http.Unauthorized, call, cp, method='PUT', document='user', guid=tests.UID, principal='fake', content={'name': 'user2'}) call(cp, method='PUT', document='user', guid=tests.UID, principal=tests.UID, content={'name': 'user2'}) self.assertEqual('user2', call(cp, method='GET', document='user', guid=tests.UID, prop='name')) + def test_authorize_Config(self): + self.touch(('authorization.conf', [ + '[%s]' % tests.UID, + 'root = True', + ])) + + class Routes(NodeRoutes): + + @route('PROBE', acl=ACL.SUPERUSER) + def probe(self): + return 'ok' + + volume = db.Volume('db', [User]) + volume['user'].create({'guid': tests.UID, 'name': 'user', 'color': '', 'pubkey': tests.PUBKEY}) + volume['user'].create({'guid': tests.UID2, 'name': 'test', 'color': '', 'pubkey': tests.PUBKEY2}) + cp = Routes('guid', volume) + + self.assertRaises(http.Forbidden, call, cp, method='PROBE') + self.assertRaises(http.Forbidden, call, cp, method='PROBE', principal=tests.UID2) + self.assertEqual('ok', call(cp, method='PROBE', principal=tests.UID)) + + def test_authorize_FullWriteForRoot(self): + self.touch(('authorization.conf', [ + '[%s]' % tests.UID2, + 'root = True', + ])) + + class Routes(NodeRoutes): + + @route('PROBE', [None, None], acl=ACL.AUTHOR) + def probe(self): + pass + + class Document(db.Resource): + pass + + volume = db.Volume('db', [User, Document]) + volume['user'].create({'guid': tests.UID, 'name': 'user', 'color': '', 'pubkey': tests.PUBKEY}) + volume['user'].create({'guid': tests.UID2, 'name': 'user', 'color': '', 'pubkey': tests.PUBKEY2}) + cp = Routes('guid', volume) + + guid = call(cp, method='POST', document='document', principal=tests.UID, content={}) + + call(cp, 'PROBE', document='document', guid=guid, principal=tests.UID) + call(cp, 'PROBE', document='document', guid=guid, principal=tests.UID2) + + def test_authorize_LiveConfigUpdates(self): + + class Routes(NodeRoutes): + + @route('PROBE', acl=ACL.SUPERUSER) + def probe(self): + pass + + volume = db.Volume('db', [User]) + volume['user'].create({'guid': tests.UID, 'name': 'user', 'color': '', 'pubkey': tests.PUBKEY}) + cp = Routes('guid', volume) + + self.assertRaises(http.Forbidden, call, cp, 'PROBE', principal=tests.UID) + self.touch(('authorization.conf', [ + '[%s]' % tests.UID, + 'root = True', + ])) + call(cp, 'PROBE', principal=tests.UID) + + def test_authorize_Anonymous(self): + + class Routes(NodeRoutes): + + @route('PROBE1', acl=ACL.AUTH) + def probe1(self, request): + pass + + @route('PROBE2', acl=ACL.SUPERUSER) + def probe2(self, request): + pass + + volume = db.Volume('db', [User]) + cp = Routes('guid', volume) + + self.assertRaises(http.Unauthorized, call, cp, 'PROBE1') + self.assertRaises(http.Forbidden, call, cp, 'PROBE2') + + self.touch(('authorization.conf', [ + '[anonymous]', + 'user = True', + 'root = True', + ])) + call(cp, 'PROBE1') + call(cp, 'PROBE2') + def test_SetUser(self): - cp = NodeCommands('guid', Volume('db')) + volume = db.Volume('db', model.RESOURCES) + volume['user'].create({'guid': tests.UID, 'name': 'user', 'color': '', 'pubkey': tests.PUBKEY}) + cp = NodeRoutes('guid', volume) - guid = call(cp, method='POST', document='context', principal='principal', content={ + guid = call(cp, method='POST', document='context', principal=tests.UID, content={ 'type': 'activity', 'title': 'title', 'summary': 'summary', 'description': 'description', }) self.assertEqual( - [{'name': 'principal', 'role': 2}], + [{'guid': tests.UID, 'name': 'user', 'role': 3}], call(cp, method='GET', document='context', guid=guid, prop='author')) def test_find_MaxLimit(self): - cp = NodeCommands('guid', Volume('db')) + volume = db.Volume('db', model.RESOURCES) + volume['user'].create({'guid': tests.UID, 'name': 'user', 'color': '', 'pubkey': tests.PUBKEY}) + cp = NodeRoutes('guid', volume) - call(cp, method='POST', document='context', principal='principal', content={ + call(cp, method='POST', document='context', principal=tests.UID, content={ 'type': 'activity', 'title': 'title1', 'summary': 'summary', 'description': 'description', }) - call(cp, method='POST', document='context', principal='principal', content={ + call(cp, method='POST', document='context', principal=tests.UID, content={ 'type': 'activity', 'title': 'title2', 'summary': 'summary', 'description': 'description', }) - call(cp, method='POST', document='context', principal='principal', content={ + call(cp, method='POST', document='context', principal=tests.UID, content={ 'type': 'activity', 'title': 'title3', 'summary': 'summary', @@ -305,10 +409,11 @@ class NodeTest(tests.Test): self.assertEqual(1, len(call(cp, method='GET', document='context', limit=1024)['result'])) def test_DeletedDocuments(self): - volume = Volume('db') - cp = NodeCommands('guid', volume) + volume = db.Volume('db', model.RESOURCES) + volume['user'].create({'guid': tests.UID, 'name': 'user', 'color': '', 'pubkey': tests.PUBKEY}) + cp = NodeRoutes('guid', volume) - guid = call(cp, method='POST', document='context', principal='principal', content={ + guid = call(cp, method='POST', document='context', principal=tests.UID, content={ 'type': 'activity', 'title': 'title1', 'summary': 'summary', @@ -325,9 +430,10 @@ class NodeTest(tests.Test): def test_CreateGUID(self): # TODO Temporal security hole, see TODO - volume2 = Volume('db2') - cp2 = MasterCommands('guid', volume2) - call(cp2, method='POST', document='context', principal='principal', content={ + volume = db.Volume('db', model.RESOURCES) + volume['user'].create({'guid': tests.UID, 'name': 'user', 'color': '', 'pubkey': tests.PUBKEY}) + cp = NodeRoutes('guid', volume) + call(cp, method='POST', document='context', principal=tests.UID, content={ 'guid': 'foo', 'type': 'activity', 'title': 'title', @@ -336,12 +442,14 @@ class NodeTest(tests.Test): }) self.assertEqual( {'guid': 'foo', 'title': 'title'}, - call(cp2, method='GET', document='context', guid='foo', reply=['guid', 'title'])) + call(cp, method='GET', document='context', guid='foo', reply=['guid', 'title'])) def test_CreateMalformedGUID(self): - cp = MasterCommands('guid', Volume('db2')) + volume = db.Volume('db', model.RESOURCES) + volume['user'].create({'guid': tests.UID, 'name': 'user', 'color': '', 'pubkey': tests.PUBKEY}) + cp = MasterRoutes('guid', volume) - self.assertRaises(RuntimeError, call, cp, method='POST', document='context', principal='principal', content={ + self.assertRaises(RuntimeError, call, cp, method='POST', document='context', principal=tests.UID, content={ 'guid': '!?', 'type': 'activity', 'title': 'title', @@ -350,16 +458,18 @@ class NodeTest(tests.Test): }) def test_FailOnExistedGUID(self): - cp = MasterCommands('guid', Volume('db2')) + volume = db.Volume('db', model.RESOURCES) + volume['user'].create({'guid': tests.UID, 'name': 'user', 'color': '', 'pubkey': tests.PUBKEY}) + cp = MasterRoutes('guid', volume) - guid = call(cp, method='POST', document='context', principal='principal', content={ + guid = call(cp, method='POST', document='context', principal=tests.UID, content={ 'type': 'activity', 'title': 'title', 'summary': 'summary', 'description': 'description', }) - self.assertRaises(RuntimeError, call, cp, method='POST', document='context', principal='principal', content={ + self.assertRaises(RuntimeError, call, cp, method='POST', document='context', principal=tests.UID, content={ 'guid': guid, 'type': 'activity', 'title': 'title', @@ -550,16 +660,23 @@ class NodeTest(tests.Test): self.assertEqual(len(activity_info), data.get('unpack_size')) - - - - -def call(cp, principal=None, content=None, **kwargs): - request = db.Request(**kwargs) - request.principal = principal +def call(routes, method, document=None, guid=None, prop=None, principal=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) + request.update(kwargs) + request.cmd = cmd request.content = content request.environ = {'HTTP_HOST': '127.0.0.1'} - return cp.call(request, db.Response()) + if principal: + request.environ['HTTP_X_SN_LOGIN'] = principal + router = Router(routes) + return router.call(request, Response()) if __name__ == '__main__': diff --git a/tests/units/node/stats_node.py b/tests/units/node/stats_node.py index 607a380..2fa9446 100755 --- a/tests/units/node/stats_node.py +++ b/tests/units/node/stats_node.py @@ -5,25 +5,19 @@ import time from __init__ import tests -from sugar_network.toolkit.rrd import Rrd +from sugar_network import db, model from sugar_network.node.stats_node import stats_node_step, Sniffer -from sugar_network.resources.user import User -from sugar_network.resources.context import Context -from sugar_network.resources.implementation import Implementation -from sugar_network.resources.review import Review -from sugar_network.resources.feedback import Feedback -from sugar_network.resources.artifact import Artifact -from sugar_network.resources.solution import Solution -from sugar_network.resources.volume import Volume +from sugar_network.toolkit.rrd import Rrd +from sugar_network.toolkit.router import Request class StatsTest(tests.Test): - def test_DoNotLogAnonymouses(self): - volume = Volume('local', [User, Context, Review, Feedback, Solution, Artifact]) + def ___test_DoNotLogAnonymouses(self): + volume = db.Volume('local', model.RESOURCES) stats = Sniffer(volume) - request = db.Request(method='GET', document='context', guid='guid') + request = Request(method='GET', path=['context', 'guid']) stats.log(request) self.assertEqual(0, stats._stats['context'].viewed) @@ -32,20 +26,20 @@ class StatsTest(tests.Test): self.assertEqual(1, stats._stats['context'].viewed) def test_DoNotLogCmds(self): - volume = Volume('local', [User, Context, Review, Feedback, Solution, Artifact]) + volume = db.Volume('local', model.RESOURCES) stats = Sniffer(volume) - request = db.Request(method='GET', document='context', guid='guid', cmd='probe') + request = Request(method='GET', path=['context', 'guid'], cmd='probe') request.principal = 'user' stats.log(request) self.assertEqual(0, stats._stats['context'].viewed) - del request['cmd'] + request.cmd = None stats.log(request) self.assertEqual(1, stats._stats['context'].viewed) def test_InitializeTotals(self): - volume = Volume('local', [User, Context, Review, Feedback, Solution, Artifact]) + volume = db.Volume('local', model.RESOURCES) stats = Sniffer(volume) self.assertEqual(0, stats._stats['user'].total) @@ -74,10 +68,10 @@ class StatsTest(tests.Test): self.assertEqual(1, stats._stats['artifact'].total) def test_POSTs(self): - volume = Volume('local', [User, Context, Review, Feedback, Solution, Artifact]) + volume = db.Volume('local', model.RESOURCES) stats = Sniffer(volume) - request = db.Request(method='POST', document='context') + request = Request(method='POST', path=['context']) request.principal = 'user' stats.log(request) stats.log(request) @@ -88,10 +82,10 @@ class StatsTest(tests.Test): self.assertEqual(0, stats._stats['context'].deleted) def test_PUTs(self): - volume = Volume('local', [User, Context, Review, Feedback, Solution, Artifact]) + volume = db.Volume('local', model.RESOURCES) stats = Sniffer(volume) - request = db.Request(method='PUT', document='context', guid='guid') + request = Request(method='PUT', path=['context', 'guid']) request.principal = 'user' stats.log(request) stats.log(request) @@ -102,10 +96,10 @@ class StatsTest(tests.Test): self.assertEqual(0, stats._stats['context'].deleted) def test_DELETEs(self): - volume = Volume('local', [User, Context, Review, Feedback, Solution, Artifact]) + volume = db.Volume('local', model.RESOURCES) stats = Sniffer(volume) - request = db.Request(method='DELETE', document='context') + request = Request(method='DELETE', path=['context']) request.principal = 'user' stats.log(request) stats.log(request) @@ -116,29 +110,29 @@ class StatsTest(tests.Test): self.assertEqual(3, stats._stats['context'].deleted) def test_GETs(self): - volume = Volume('local', [User, Context, Review, Feedback, Solution, Artifact]) + volume = db.Volume('local', model.RESOURCES) stats = Sniffer(volume) - request = db.Request(method='GET', document='user') + request = Request(method='GET', path=['user']) request.principal = 'user' stats.log(request) self.assertEqual(0, stats._stats['user'].viewed) def test_GETsDocument(self): - volume = Volume('local', [User, Context, Review, Feedback, Solution, Artifact]) + volume = db.Volume('local', model.RESOURCES) stats = Sniffer(volume) - request = db.Request(method='GET', document='user', guid='user') + request = Request(method='GET', path=['user', 'user']) request.principal = 'user' stats.log(request) self.assertEqual(1, stats._stats['user'].viewed) def test_FeedbackSolutions(self): - volume = Volume('local', [User, Context, Review, Feedback, Solution, Artifact]) + volume = db.Volume('local', model.RESOURCES) stats = Sniffer(volume) volume['feedback'].create({'guid': 'guid', 'context': 'context', 'type': 'idea', 'title': '', 'content': ''}) - request = db.Request(method='PUT', document='feedback', guid='guid') + request = Request(method='PUT', path=['feedback', 'guid']) request.principal = 'user' request.content = {} stats.log(request) @@ -162,51 +156,51 @@ class StatsTest(tests.Test): self.assertEqual(0, stats._stats['feedback'].solutions) def test_Comments(self): - volume = Volume('local', [User, Context, Review, Feedback, Solution, Artifact]) + volume = db.Volume('local', model.RESOURCES) stats = Sniffer(volume) volume['solution'].create({'guid': 'solution', 'context': 'context', 'feedback': 'feedback', 'content': ''}) volume['feedback'].create({'guid': 'feedback', 'context': 'context', 'type': 'idea', 'title': '', 'content': ''}) volume['review'].create({'guid': 'review', 'context': 'context', 'title': '', 'content': '', 'rating': 5}) - request = db.Request(method='POST', document='comment') + request = Request(method='POST', path=['comment']) request.principal = 'user' request.content = {'solution': 'solution'} stats.log(request) self.assertEqual(1, stats._stats['solution'].commented) - request = db.Request(method='POST', document='comment') + request = Request(method='POST', path=['comment']) request.principal = 'user' request.content = {'feedback': 'feedback'} stats.log(request) self.assertEqual(1, stats._stats['feedback'].commented) - request = db.Request(method='POST', document='comment') + request = Request(method='POST', path=['comment']) request.principal = 'user' request.content = {'review': 'review'} stats.log(request) self.assertEqual(1, stats._stats['review'].commented) def test_Reviewes(self): - volume = Volume('local', [User, Context, Review, Feedback, Solution, Artifact]) + volume = db.Volume('local', model.RESOURCES) stats = Sniffer(volume) volume['context'].create({'guid': 'context', 'type': 'activity', 'title': '', 'summary': '', 'description': ''}) volume['artifact'].create({'guid': 'artifact', 'type': 'instance', 'context': 'context', 'title': '', 'description': ''}) - request = db.Request(method='POST', document='review') + request = Request(method='POST', path=['review']) request.principal = 'user' request.content = {'context': 'context', 'rating': 0} stats.log(request) self.assertEqual(1, stats._stats['context'].reviewed) self.assertEqual(0, stats._stats['artifact'].reviewed) - request = db.Request(method='POST', document='review') + request = Request(method='POST', path=['review']) request.principal = 'user' request.content = {'context': 'context', 'artifact': '', 'rating': 0} stats.log(request) self.assertEqual(2, stats._stats['context'].reviewed) self.assertEqual(0, stats._stats['artifact'].reviewed) - request = db.Request(method='POST', document='review') + request = Request(method='POST', path=['review']) request.principal = 'user' request.content = {'artifact': 'artifact', 'rating': 0} stats.log(request) @@ -214,55 +208,55 @@ class StatsTest(tests.Test): self.assertEqual(1, stats._stats['artifact'].reviewed) def test_ContextDownloaded(self): - volume = Volume('local', [User, Context, Review, Feedback, Solution, Artifact, Implementation]) + volume = db.Volume('local', model.RESOURCES) stats = Sniffer(volume) volume['context'].create({'guid': 'context', 'type': 'activity', 'title': '', 'summary': '', 'description': ''}) volume['implementation'].create({'guid': 'implementation', 'context': 'context', 'license': 'GPLv3', 'version': '1', 'date': 0, 'stability': 'stable', 'notes': ''}) - request = db.Request(method='GET', document='implementation', guid='implementation', prop='fake') + request = Request(method='GET', path=['implementation', 'implementation', 'fake']) request.principal = 'user' stats.log(request) self.assertEqual(0, stats._stats['context'].downloaded) - request = db.Request(method='GET', document='implementation', guid='implementation', prop='data') + request = Request(method='GET', path=['implementation', 'implementation', 'data']) request.principal = 'user' stats.log(request) self.assertEqual(1, stats._stats['context'].downloaded) def test_ContextReleased(self): - volume = Volume('local', [User, Context, Review, Feedback, Solution, Artifact, Implementation]) + volume = db.Volume('local', model.RESOURCES) stats = Sniffer(volume) volume['context'].create({'guid': 'context', 'type': 'activity', 'title': '', 'summary': '', 'description': ''}) - request = db.Request(method='POST', document='implementation') + request = Request(method='POST', path=['implementation']) request.principal = 'user' request.content = {'context': 'context'} stats.log(request) self.assertEqual(1, stats._stats['context'].released) def test_ContextFailed(self): - volume = Volume('local', [User, Context, Review, Feedback, Solution, Artifact, Implementation]) + volume = db.Volume('local', model.RESOURCES) stats = Sniffer(volume) volume['context'].create({'guid': 'context', 'type': 'activity', 'title': '', 'summary': '', 'description': ''}) - request = db.Request(method='POST', document='report') + 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_ContextActive(self): - volume = Volume('local', [User, Context, Review, Feedback, Solution, Artifact, Implementation]) + volume = db.Volume('local', model.RESOURCES) stats = Sniffer(volume) - request = db.Request(method='PUT', document='context', guid='1') + request = Request(method='PUT', path=['context', '1']) request.principal = 'user' stats.log(request) self.assertEqual( ['1'], stats._stats['context'].active.keys()) - request = db.Request(method='GET', document='artifact', context='2') + request = Request(method='GET', path=['artifact'], context='2') request.principal = 'user' stats.log(request) self.assertEqual( @@ -270,7 +264,7 @@ class StatsTest(tests.Test): stats._stats['context'].active.keys()) volume['artifact'].create({'guid': 'artifact', 'type': 'instance', 'context': '3', 'title': '', 'description': ''}) - request = db.Request(method='GET', document='review', artifact='artifact') + request = Request(method='GET', path=['review'], artifact='artifact') request.principal = 'user' stats.log(request) self.assertEqual( @@ -278,21 +272,21 @@ class StatsTest(tests.Test): sorted(stats._stats['context'].active.keys())) volume['feedback'].create({'guid': 'feedback', 'context': '4', 'type': 'idea', 'title': '', 'content': ''}) - request = db.Request(method='GET', document='solution', feedback='feedback') + request = Request(method='GET', path=['solution'], feedback='feedback') request.principal = 'user' stats.log(request) self.assertEqual( ['1', '2', '3', '4'], sorted(stats._stats['context'].active.keys())) - request = db.Request(method='GET', document='context', guid='5') + request = Request(method='GET', path=['context', '5']) request.principal = 'user' stats.log(request) self.assertEqual( ['1', '2', '3', '4', '5'], sorted(stats._stats['context'].active.keys())) - request = db.Request(method='POST', document='report') + request = Request(method='POST', path=['report']) request.principal = 'user' request.content = {'context': '6'} stats.log(request) @@ -301,7 +295,7 @@ class StatsTest(tests.Test): sorted(stats._stats['context'].active.keys())) volume['solution'].create({'guid': 'solution', 'context': '7', 'feedback': 'feedback', 'content': ''}) - request = db.Request(method='POST', document='comment') + request = Request(method='POST', path=['comment']) request.principal = 'user' request.content = {'solution': 'solution'} stats.log(request) @@ -310,10 +304,10 @@ class StatsTest(tests.Test): sorted(stats._stats['context'].active.keys())) def test_UserActive(self): - volume = Volume('local', [User, Context, Review, Feedback, Solution, Artifact, Implementation]) + volume = db.Volume('local', model.RESOURCES) stats = Sniffer(volume) - request = db.Request(method='GET', document='user') + request = Request(method='GET', path=['user']) request.principal = '1' stats.log(request) self.assertEqual( @@ -323,7 +317,7 @@ class StatsTest(tests.Test): set([]), stats._stats['user'].effective) - request = db.Request(method='POST', document='user') + request = Request(method='POST', path=['user']) request.principal = '2' stats.log(request) self.assertEqual( @@ -334,17 +328,17 @@ class StatsTest(tests.Test): stats._stats['user'].effective) def test_ArtifactDownloaded(self): - volume = Volume('local', [User, Context, Review, Feedback, Solution, Artifact]) + volume = db.Volume('local', model.RESOURCES) stats = Sniffer(volume) volume['artifact'].create({'guid': 'artifact', 'type': 'instance', 'context': 'context', 'title': '', 'description': ''}) - request = db.Request(method='GET', document='artifact', guid='artifact', prop='fake') + request = Request(method='GET', path=['artifact', 'artifact', 'fake']) request.principal = 'user' stats.log(request) self.assertEqual(0, stats._stats['artifact'].viewed) self.assertEqual(0, stats._stats['artifact'].downloaded) - request = db.Request(method='GET', document='artifact', guid='artifact', prop='data') + request = Request(method='GET', path=['artifact', 'artifact', 'data']) request.principal = 'user' stats.log(request) self.assertEqual(0, stats._stats['artifact'].viewed) @@ -352,7 +346,7 @@ class StatsTest(tests.Test): def test_Commit(self): stats_node_step.value = 1 - volume = Volume('local', [User, Context, Review, Feedback, Solution, Artifact]) + volume = db.Volume('local', model.RESOURCES) volume['user'].create({'guid': 'user', 'name': 'user', 'color': '', 'pubkey': ''}) volume['context'].create({'guid': 'context', 'type': 'activity', 'title': '', 'summary': '', 'description': ''}) volume['review'].create({'guid': 'review', 'context': 'context', 'title': '', 'content': '', 'rating': 5}) @@ -361,22 +355,22 @@ class StatsTest(tests.Test): volume['artifact'].create({'guid': 'artifact', 'type': 'instance', 'context': 'context', 'title': '', 'description': ''}) stats = Sniffer(volume) - request = db.Request(method='GET', document='user', guid='user') + request = Request(method='GET', path=['user', 'user']) request.principal = 'user' stats.log(request) - request = db.Request(method='GET', document='context', guid='context') + request = Request(method='GET', path=['context', 'context']) request.principal = 'user' stats.log(request) - request = db.Request(method='GET', document='review', guid='review') + request = Request(method='GET', path=['review', 'review']) request.principal = 'user' stats.log(request) - request = db.Request(method='GET', document='feedback', guid='feedback') + request = Request(method='GET', path=['feedback', 'feedback']) request.principal = 'user' stats.log(request) - request = db.Request(method='GET', document='solution', guid='solution') + request = Request(method='GET', path=['solution', 'solution']) request.principal = 'user' stats.log(request) - request = db.Request(method='GET', document='artifact', guid='artifact') + request = Request(method='GET', path=['artifact', 'artifact']) request.principal = 'user' stats.log(request) @@ -474,11 +468,11 @@ class StatsTest(tests.Test): 'released': 0.0, })], ], - [[(db.name,) + i for i in db.get(db.first, db.last)] for db in Rrd('stats/node', 1)]) + [[(j.name,) + i for i in j.get(j.last, j.last)] for j in Rrd('stats/node', 1)]) def test_CommitContextStats(self): stats_node_step.value = 1 - volume = Volume('local', [User, Context, Review, Feedback, Solution, Artifact, Implementation]) + volume = db.Volume('local', model.RESOURCES) volume['context'].create({'guid': 'context', 'type': 'activity', 'title': '', 'summary': '', 'description': ''}) volume['implementation'].create({'guid': 'implementation', 'context': 'context', 'license': 'GPLv3', 'version': '1', 'date': 0, 'stability': 'stable', 'notes': ''}) @@ -488,14 +482,14 @@ class StatsTest(tests.Test): self.assertEqual(0, volume['context'].get('context')['rating']) stats = Sniffer(volume) - request = db.Request(method='GET', document='implementation', guid='implementation', prop='data') + request = Request(method='GET', path=['implementation', 'implementation', 'data']) request.principal = 'user' stats.log(request) - request = db.Request(method='POST', document='review') + request = Request(method='POST', path=['review']) request.principal = 'user' request.content = {'context': 'context', 'rating': 5} stats.log(request) - request = db.Request(method='POST', document='review') + request = Request(method='POST', path=['review']) request.principal = 'user' request.content = {'artifact': 'artifact', 'rating': 5} stats.log(request) @@ -510,10 +504,10 @@ class StatsTest(tests.Test): self.assertEqual(5, volume['context'].get('context')['rating']) stats = Sniffer(volume) - request = db.Request(method='GET', document='implementation', guid='implementation', prop='data') + request = Request(method='GET', path=['implementation', 'implementation', 'data']) request.principal = 'user' stats.log(request) - request = db.Request(method='POST', document='review') + request = Request(method='POST', path=['review']) request.principal = 'user' request.content = {'context': 'context', 'rating': 1} stats.log(request) @@ -524,7 +518,7 @@ class StatsTest(tests.Test): def test_CommitArtifactStats(self): stats_node_step.value = 1 - volume = Volume('local', [User, Context, Review, Feedback, Solution, Artifact, Implementation]) + volume = db.Volume('local', model.RESOURCES) volume['context'].create({'guid': 'context', 'type': 'activity', 'title': '', 'summary': '', 'description': ''}) volume['artifact'].create({'guid': 'artifact', 'type': 'instance', 'context': 'context', 'title': '', 'description': ''}) @@ -533,10 +527,10 @@ class StatsTest(tests.Test): self.assertEqual(0, volume['artifact'].get('artifact')['rating']) stats = Sniffer(volume) - request = db.Request(method='GET', document='artifact', guid='artifact', prop='data') + request = Request(method='GET', path=['artifact', 'artifact', 'data']) request.principal = 'user' stats.log(request) - request = db.Request(method='POST', document='review') + request = Request(method='POST', path=['review']) request.principal = 'user' request.content = {'artifact': 'artifact', 'rating': 5} stats.log(request) @@ -550,10 +544,10 @@ class StatsTest(tests.Test): self.assertEqual([1, 5], volume['artifact'].get('artifact')['reviews']) self.assertEqual(5, volume['artifact'].get('artifact')['rating']) - request = db.Request(method='GET', document='artifact', guid='artifact', prop='data') + request = Request(method='GET', path=['artifact', 'artifact', 'data']) request.principal = 'user' stats.log(request) - request = db.Request(method='POST', document='review') + request = Request(method='POST', path=['review']) request.principal = 'user' request.content = {'artifact': 'artifact', 'rating': 1} stats.log(request) diff --git a/tests/units/node/sync_master.py b/tests/units/node/sync_master.py index e137f6e..c946396 100755 --- a/tests/units/node/sync_master.py +++ b/tests/units/node/sync_master.py @@ -18,10 +18,11 @@ from __init__ import tests from sugar_network.db.directory import Directory from sugar_network import db, node, toolkit from sugar_network.node import sync -from sugar_network.node.master import MasterCommands -from sugar_network.resources.volume import Volume -from sugar_network.toolkit import coroutine, util +from sugar_network.node.master import MasterRoutes +from sugar_network.db.volume import Volume +from sugar_network.toolkit import coroutine from sugar_network.toolkit.rrd import Rrd +from sugar_network.toolkit.router import Response class statvfs(object): @@ -40,7 +41,7 @@ class SyncMasterTest(tests.Test): self.override(os, 'statvfs', lambda *args: statvfs()) statvfs.f_bfree = 999999999 - class Document(db.Document): + class Document(db.Resource): @db.indexed_property(slot=1, default='') def prop(self, value): @@ -48,7 +49,7 @@ class SyncMasterTest(tests.Test): node.files_root.value = 'sync' self.volume = Volume('master', [Document]) - self.master = MasterCommands('127.0.0.1:8888', self.volume) + self.master = MasterRoutes('127.0.0.1:8888', self.volume) def next_uuid(self): self.uuid += 1 @@ -58,7 +59,7 @@ class SyncMasterTest(tests.Test): request = Request() for chunk in sync.encode([ ('diff', None, [ - {'document': 'document'}, + {'resource': 'document'}, {'guid': '1', 'diff': { 'guid': {'value': '1', 'mtime': 1}, 'ctime': {'value': 1, 'mtime': 1}, @@ -78,7 +79,7 @@ class SyncMasterTest(tests.Test): response.seek(0) self.assertEqual([ ({'packet': 'ack', 'ack': [[1, 1]], 'src': '127.0.0.1:8888', 'sequence': [[1, 1]], 'dst': None}, []), - ({'packet': 'diff', 'src': '127.0.0.1:8888'}, [{'document': 'document'}, {'commit': []}]), + ({'packet': 'diff', 'src': '127.0.0.1:8888'}, [{'resource': 'document'}, {'commit': []}]), ], [(packet.props, [i for i in packet]) for packet in sync.decode(response)]) @@ -86,7 +87,7 @@ class SyncMasterTest(tests.Test): for chunk in sync.encode([ ('pull', {'sequence': [[1, None]]}, None), ('diff', None, [ - {'document': 'document'}, + {'resource': 'document'}, {'guid': '2', 'diff': { 'guid': {'value': '2', 'mtime': 2}, 'ctime': {'value': 2, 'mtime': 2}, @@ -106,7 +107,7 @@ class SyncMasterTest(tests.Test): self.assertEqual([ ({'packet': 'ack', 'ack': [[2, 2]], 'src': '127.0.0.1:8888', 'sequence': [[2, 2]], 'dst': None}, []), ({'packet': 'diff', 'src': '127.0.0.1:8888'}, [ - {'document': 'document'}, + {'resource': 'document'}, {'guid': '1', 'diff': { 'guid': {'value': '1', 'mtime': 1}, 'ctime': {'value': 1, 'mtime': 1}, @@ -143,7 +144,7 @@ class SyncMasterTest(tests.Test): request = Request() for chunk in sync.package_encode([ ('diff', None, [ - {'document': 'document'}, + {'resource': 'document'}, {'guid': '1', 'diff': { 'guid': {'value': '1', 'mtime': 1}, 'ctime': {'value': 1, 'mtime': 1}, @@ -161,7 +162,7 @@ class SyncMasterTest(tests.Test): request.content_stream.write(chunk) request.content_stream.seek(0) - response = db.Response() + response = Response() reply = StringIO() for chunk in self.master.push(request, response): reply.write(chunk) @@ -186,7 +187,7 @@ class SyncMasterTest(tests.Test): ('pull', {'sequence': [[1, None]]}, None), ('files_pull', {'sequence': [[1, None]]}, None), ('diff', None, [ - {'document': 'document'}, + {'resource': 'document'}, {'guid': '2', 'diff': { 'guid': {'value': '2', 'mtime': 2}, 'ctime': {'value': 2, 'mtime': 2}, @@ -204,7 +205,7 @@ class SyncMasterTest(tests.Test): request.content_stream.write(chunk) request.content_stream.seek(0) - response = db.Response() + response = Response() reply = StringIO() for chunk in self.master.push(request, response): reply.write(chunk) @@ -235,7 +236,7 @@ class SyncMasterTest(tests.Test): ], dst='127.0.0.1:8888'): request.content_stream.write(chunk) request.content_stream.seek(0) - response = db.Response() + response = Response() reply = StringIO() for chunk in self.master.push(request, response): reply.write(chunk) @@ -259,7 +260,7 @@ class SyncMasterTest(tests.Test): ], dst='127.0.0.1:8888'): request.content_stream.write(chunk) request.content_stream.seek(0) - response = db.Response() + response = Response() reply = StringIO() for chunk in self.master.push(request, response): reply.write(chunk) @@ -281,7 +282,7 @@ class SyncMasterTest(tests.Test): for chunk in sync.package_encode([ ('pull', {'sequence': [[1, None]]}, None), ('diff', None, [ - {'document': 'document'}, + {'resource': 'document'}, {'guid': '1', 'diff': { 'guid': {'value': '1', 'mtime': 1}, 'ctime': {'value': 1, 'mtime': 1}, @@ -294,7 +295,7 @@ class SyncMasterTest(tests.Test): request.content_stream.write(chunk) request.content_stream.seek(0) - response = db.Response() + response = Response() reply = StringIO() for chunk in self.master.push(request, response): reply.write(chunk) @@ -322,7 +323,7 @@ class SyncMasterTest(tests.Test): request = Request() request.environ['HTTP_COOKIE'] = 'sugar_network_pull=%s' % \ base64.b64encode(json.dumps([('pull', None, [[1, None]]), ('files_pull', None, [[1, None]])])) - response = db.Response() + response = Response() self.assertEqual(None, self.master.pull(request, response)) self.assertEqual([ 'sugar_network_pull=%s; Max-Age=3600; HttpOnly' % \ @@ -336,14 +337,14 @@ class SyncMasterTest(tests.Test): request = Request() request.environ['HTTP_COOKIE'] = response.get('set-cookie')[0] - response = db.Response() + response = Response() reply = StringIO() for chunk in self.master.pull(request, response): reply.write(chunk) reply.seek(0) self.assertEqual([ ({'packet': 'diff'}, [ - {'document': 'document'}, + {'resource': 'document'}, {'guid': '1', 'diff': { 'prop': {'value': '1', 'mtime': 0}, 'guid': {'value': '1', 'mtime': 0}, @@ -371,7 +372,7 @@ class SyncMasterTest(tests.Test): request = Request() request.environ['HTTP_COOKIE'] = response.get('set-cookie')[0] - response = db.Response() + response = Response() reply = StringIO() for chunk in self.master.pull(request, response): reply.write(chunk) @@ -401,7 +402,7 @@ class SyncMasterTest(tests.Test): request = Request() request.environ['HTTP_COOKIE'] = 'sugar_network_pull=%s' % \ base64.b64encode(json.dumps([('pull', None, [[1, None]])])) - response = db.Response() + response = Response() self.assertEqual(None, self.master.pull(request, response)) self.assertEqual([ 'sugar_network_pull=%s; Max-Age=3600; HttpOnly' % \ @@ -416,7 +417,7 @@ class SyncMasterTest(tests.Test): request = Request() request.environ['HTTP_COOKIE'] = response.get('set-cookie')[0] - response = db.Response() + response = Response() self.assertEqual(None, self.master.pull(request, response)) self.assertEqual([ 'sugar_network_pull=unset_sugar_network_pull; Max-Age=0; HttpOnly', @@ -438,7 +439,7 @@ class SyncMasterTest(tests.Test): } request = Request() - response = db.Response() + response = Response() self.assertEqual(None, self.master.pull(request, response)) self.assertEqual([ 'sugar_network_pull=%s; Max-Age=3600; HttpOnly' % \ @@ -452,7 +453,7 @@ class SyncMasterTest(tests.Test): request = Request() request.environ['HTTP_COOKIE'] = response.get('set-cookie')[0] - response = db.Response() + response = Response() reply = StringIO() for chunk in self.master.pull(request, response): reply.write(chunk) @@ -472,7 +473,7 @@ class SyncMasterTest(tests.Test): request = Request() request.environ['HTTP_COOKIE'] = response.get('set-cookie')[0] - response = db.Response() + response = Response() reply = StringIO() for chunk in self.master.pull(request, response): reply.write(chunk) @@ -496,7 +497,7 @@ class SyncMasterTest(tests.Test): ('pull', {'src': '2', 'sequence': [[2, None]], 'layer': '2'}, None), ('pull', {'src': '2', 'sequence': [[22, None]], 'layer': '2'}, None), ('diff', {'src': '3'}, [ - {'document': 'document'}, + {'resource': 'document'}, {'guid': '1', 'diff': { 'guid': {'value': '1', 'mtime': 1}, 'ctime': {'value': 1, 'mtime': 1}, @@ -506,7 +507,7 @@ class SyncMasterTest(tests.Test): {'commit': [[1, 1]]}, ]), ('diff', {'src': '3'}, [ - {'document': 'document'}, + {'resource': 'document'}, {'guid': '2', 'diff': { 'guid': {'value': '2', 'mtime': 2}, 'ctime': {'value': 2, 'mtime': 2}, @@ -519,7 +520,7 @@ class SyncMasterTest(tests.Test): request.content_stream.write(chunk) request.content_stream.seek(0) - response = db.Response() + response = Response() reply = StringIO() for chunk in self.master.push(request, response): reply.write(chunk) @@ -548,7 +549,7 @@ class SyncMasterTest(tests.Test): 'sugar_network_pull': base64.b64encode(json.dumps([('pull', None, [[1, None]])])), 'sugar_network_sent': base64.b64encode(json.dumps({'slave': [[2, 2]]})), } - response = db.Response() + response = Response() self.assertEqual(None, self.master.pull(request, response)) self.assertEqual([ 'sugar_network_pull=%s; Max-Age=3600; HttpOnly' % \ @@ -568,7 +569,7 @@ class SyncMasterTest(tests.Test): reply.seek(0) self.assertEqual([ ({'packet': 'diff'}, [ - {'document': 'document'}, + {'resource': 'document'}, {'guid': '1', 'diff': { 'prop': {'value': '1', 'mtime': 0}, 'guid': {'value': '1', 'mtime': 0}, @@ -596,7 +597,7 @@ class SyncMasterTest(tests.Test): 'sugar_network_pull': base64.b64encode(json.dumps([('pull', None, [[1, None]])])), 'sugar_network_sent': base64.b64encode(json.dumps({'slave': [[2, 2]], 'other': []})), } - response = db.Response() + response = Response() self.assertEqual(None, self.master.pull(request, response)) self.assertEqual([ 'sugar_network_pull=%s; Max-Age=3600; HttpOnly' % \ @@ -616,7 +617,7 @@ class SyncMasterTest(tests.Test): reply.seek(0) self.assertEqual([ ({'packet': 'diff'}, [ - {'document': 'document'}, + {'resource': 'document'}, {'guid': '1', 'diff': { 'prop': {'value': '1', 'mtime': 0}, 'guid': {'value': '1', 'mtime': 0}, diff --git a/tests/units/node/sync_offline.py b/tests/units/node/sync_offline.py index 752ff93..f3b7111 100755 --- a/tests/units/node/sync_offline.py +++ b/tests/units/node/sync_offline.py @@ -15,9 +15,9 @@ from sugar_network import db, node, toolkit from sugar_network.toolkit.rrd import Rrd from sugar_network.client import api_url from sugar_network.node import sync, stats_user, files_root -from sugar_network.node.slave import SlaveCommands -from sugar_network.resources.volume import Volume -from sugar_network.toolkit import coroutine, util +from sugar_network.node.slave import SlaveRoutes +from sugar_network.db import Volume +from sugar_network.toolkit import coroutine class statvfs(object): @@ -44,12 +44,12 @@ class SyncOfflineTest(tests.Test): def test_FailOnFullDump(self): - class Document(db.Document): + class Document(db.Resource): pass volume = Volume('node', [Document]) - util.ensure_key('node/key') - cp = SlaveCommands('node/key', volume) + toolkit.ensure_key('node/key') + cp = SlaveRoutes('node/key', volume) node.sync_layers.value = None self.assertRaises(RuntimeError, cp.offline_sync, tests.tmpdir + '/mnt') @@ -62,12 +62,12 @@ class SyncOfflineTest(tests.Test): def test_Export(self): - class Document(db.Document): + class Document(db.Resource): pass volume = Volume('node', [Document]) - util.ensure_key('node/key') - cp = SlaveCommands('node/key', volume) + toolkit.ensure_key('node/key') + cp = SlaveRoutes('node/key', volume) stats_user.stats_user.value = True volume['document'].create({'guid': '1', 'prop': 'value1', 'ctime': 1, 'mtime': 1}) @@ -83,7 +83,7 @@ class SyncOfflineTest(tests.Test): self.assertEqual([ ({'packet': 'diff', 'src': cp.guid, 'dst': '127.0.0.1:8888', 'api_url': 'http://127.0.0.1:8888', 'session': '1', 'filename': '2.sneakernet'}, [ - {'document': 'document'}, + {'resource': 'document'}, {'guid': '1', 'diff': { 'guid': {'value': '1', 'mtime': 0}, 'ctime': {'value': 1, 'mtime': 0}, @@ -111,15 +111,15 @@ class SyncOfflineTest(tests.Test): def test_ContinuesExport(self): payload = ''.join([str(uuid.uuid4()) for i in xrange(5000)]) - class Document(db.Document): + class Document(db.Resource): @db.indexed_property(slot=1) def prop(self, value): return value volume = Volume('node', [Document]) - util.ensure_key('node/key') - cp = SlaveCommands('node/key', volume) + toolkit.ensure_key('node/key') + cp = SlaveRoutes('node/key', volume) stats_user.stats_user.value = True volume['document'].create({'guid': '1', 'prop': payload, 'ctime': 1, 'mtime': 1}) @@ -136,7 +136,7 @@ class SyncOfflineTest(tests.Test): self.assertEqual([ ({'packet': 'diff', 'src': cp.guid, 'dst': '127.0.0.1:8888', 'api_url': 'http://127.0.0.1:8888', 'session': '1', 'filename': '2.sneakernet'}, [ - {'document': 'document'}, + {'resource': 'document'}, {'guid': '1', 'diff': { 'guid': {'value': '1', 'mtime': 0}, 'ctime': {'value': 1, 'mtime': 0}, @@ -156,7 +156,7 @@ class SyncOfflineTest(tests.Test): self.assertEqual([ ({'packet': 'diff', 'src': cp.guid, 'dst': '127.0.0.1:8888', 'api_url': 'http://127.0.0.1:8888', 'session': '1', 'filename': '3.sneakernet'}, [ - {'document': 'document'}, + {'resource': 'document'}, {'guid': '2', 'diff': { 'guid': {'value': '2', 'mtime': 0}, 'ctime': {'value': 2, 'mtime': 0}, @@ -179,7 +179,7 @@ class SyncOfflineTest(tests.Test): self.assertEqual([ ({'packet': 'diff', 'src': cp.guid, 'dst': '127.0.0.1:8888', 'api_url': 'http://127.0.0.1:8888', 'session': '4', 'filename': '5.sneakernet'}, [ - {'document': 'document'}, + {'resource': 'document'}, {'guid': '1', 'diff': { 'guid': {'value': '1', 'mtime': 0}, 'ctime': {'value': 1, 'mtime': 0}, @@ -205,19 +205,19 @@ class SyncOfflineTest(tests.Test): sorted([(packet.props, [i for i in packet]) for packet in sync.sneakernet_decode('3')])) def test_Import(self): - class Document(db.Document): + class Document(db.Resource): pass volume = Volume('node', [Document]) - util.ensure_key('node/key') - cp = SlaveCommands('node/key', volume) + toolkit.ensure_key('node/key') + cp = SlaveRoutes('node/key', volume) stats_user.stats_user.value = True files_root.value = 'files' ts = int(time.time()) sync.sneakernet_encode([ ('diff', {'src': '127.0.0.1:8888'}, [ - {'document': 'document'}, + {'resource': 'document'}, {'guid': '1', 'diff': { 'guid': {'value': '1', 'mtime': 0}, 'ctime': {'value': 1, 'mtime': 0}, diff --git a/tests/units/node/sync_online.py b/tests/units/node/sync_online.py index 9f978e8..c8bf5f8 100755 --- a/tests/units/node/sync_online.py +++ b/tests/units/node/sync_online.py @@ -7,16 +7,16 @@ from os.path import exists from __init__ import tests -from sugar_network import db -from sugar_network.db.router import Router +from sugar_network import db, toolkit from sugar_network.client import Client, api_url from sugar_network.node import sync, stats_user, files_root -from sugar_network.node.master import MasterCommands -from sugar_network.node.slave import SlaveCommands -from sugar_network.resources.volume import Volume -from sugar_network.resources.user import User -from sugar_network.resources.feedback import Feedback -from sugar_network.toolkit import util, coroutine +from sugar_network.node.master import MasterRoutes +from sugar_network.node.slave import SlaveRoutes +from sugar_network.db.volume import Volume +from sugar_network.model.user import User +from sugar_network.model.feedback import Feedback +from sugar_network.toolkit.router import Router +from sugar_network.toolkit import coroutine class SyncOnlineTest(tests.Test): @@ -42,14 +42,14 @@ class SyncOnlineTest(tests.Test): files_root.value = 'master/files' self.master_volume = Volume('master', [User, Document]) - self.master_server = coroutine.WSGIServer(('127.0.0.1', 9000), Router(MasterCommands('127.0.0.1:9000', self.master_volume))) + self.master_server = coroutine.WSGIServer(('127.0.0.1', 9000), Router(MasterRoutes('127.0.0.1:9000', self.master_volume))) coroutine.spawn(self.master_server.serve_forever) coroutine.dispatch() files_root.value = 'slave/files' self.slave_volume = Volume('slave', [User, Document]) - util.ensure_key('slave/node') - self.slave_server = coroutine.WSGIServer(('127.0.0.1', 9001), Router(SlaveCommands('slave/node', self.slave_volume))) + toolkit.ensure_key('slave/node') + self.slave_server = coroutine.WSGIServer(('127.0.0.1', 9001), Router(SlaveRoutes('slave/node', self.slave_volume))) coroutine.spawn(self.slave_server.serve_forever) coroutine.dispatch() diff --git a/tests/units/node/volume.py b/tests/units/node/volume.py index 15772e1..9a3a113 100755 --- a/tests/units/node/volume.py +++ b/tests/units/node/volume.py @@ -9,18 +9,11 @@ from cStringIO import StringIO from __init__ import tests -from sugar_network import db +from sugar_network import db, toolkit, model from sugar_network.node.volume import diff, merge from sugar_network.node.stats_node import stats_node_step, Sniffer -from sugar_network.node.commands import NodeCommands -from sugar_network.resources.user import User -from sugar_network.resources.volume import Volume, Resource -from sugar_network.resources.review import Review -from sugar_network.resources.context import Context -from sugar_network.resources.artifact import Artifact -from sugar_network.resources.feedback import Feedback -from sugar_network.resources.solution import Solution -from sugar_network.toolkit import util +from sugar_network.node.routes import NodeRoutes +from sugar_network.toolkit.router import Router, Request, Response, fallbackroute, Blob, ACL, route class VolumeTest(tests.Test): @@ -28,32 +21,36 @@ 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.Document): + class Document(db.Resource): @db.indexed_property(slot=1) def prop(self, value): return value - volume = Volume('db', [Document]) - cp = NodeCommands('guid', volume) + volume = db.Volume('db', [Document]) + cp = NodeRoutes('guid', volume) - guid1 = call(cp, method='POST', document='document', principal='principal', content={'prop': 'a'}) + 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', principal='principal', content={'prop': 'b'}) + guid2 = call(cp, method='POST', document='document', content={'prop': 'b'}) self.utime('db/document/%s/%s' % (guid2[:2], guid2), 2) - in_seq = util.Sequence([[1, None]]) + in_seq = toolkit.Sequence([[1, None]]) self.assertEqual([ - {'document': 'document'}, + {'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': ['public']}, + 'tags': {'mtime': 1, 'value': []}, }, }, {'guid': guid2, @@ -62,6 +59,9 @@ class VolumeTest(tests.Test): 'mtime': {'value': 0, 'mtime': 2}, 'ctime': {'value': 0, 'mtime': 2}, 'prop': {'value': 'b', 'mtime': 2}, + 'author': {'mtime': 2, 'value': {}}, + 'layer': {'mtime': 2, 'value': ['public']}, + 'tags': {'mtime': 2, 'value': []}, }, }, {'commit': [[1, 2]]}, @@ -71,23 +71,23 @@ class VolumeTest(tests.Test): def test_diff_Partial(self): - class Document(db.Document): + class Document(db.Resource): @db.indexed_property(slot=1) def prop(self, value): return value - volume = Volume('db', [Document]) - cp = NodeCommands('guid', volume) + volume = db.Volume('db', [Document]) + cp = NodeRoutes('guid', volume) - guid1 = call(cp, method='POST', document='document', principal='principal', content={'prop': 'a'}) + 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', principal='principal', content={'prop': 'b'}) + guid2 = call(cp, method='POST', document='document', content={'prop': 'b'}) self.utime('db/document/%s/%s' % (guid2[:2], guid2), 2) - in_seq = util.Sequence([[1, None]]) + in_seq = toolkit.Sequence([[1, None]]) patch = diff(volume, in_seq) - self.assertEqual({'document': 'document'}, next(patch)) + self.assertEqual({'resource': 'document'}, next(patch)) self.assertEqual(guid1, next(patch)['guid']) self.assertEqual({'commit': []}, patch.throw(StopIteration())) try: @@ -97,7 +97,7 @@ class VolumeTest(tests.Test): pass patch = diff(volume, in_seq) - self.assertEqual({'document': 'document'}, next(patch)) + 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())) @@ -109,29 +109,29 @@ class VolumeTest(tests.Test): def test_diff_Stretch(self): - class Document(db.Document): + class Document(db.Resource): @db.indexed_property(slot=1) def prop(self, value): return value - volume = Volume('db', [Document]) - cp = NodeCommands('guid', volume) + volume = db.Volume('db', [Document]) + cp = NodeRoutes('guid', volume) - guid1 = call(cp, method='POST', document='document', principal='principal', content={'prop': 'a'}) + 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', principal='principal', content={'prop': 'b'}) + guid2 = call(cp, method='POST', document='document', content={'prop': 'b'}) volume['document'].delete(guid2) - guid3 = call(cp, method='POST', document='document', principal='principal', content={'prop': 'c'}) + 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', principal='principal', content={'prop': 'd'}) + guid4 = call(cp, method='POST', document='document', content={'prop': 'd'}) volume['document'].delete(guid4) - guid5 = call(cp, method='POST', document='document', principal='principal', content={'prop': 'f'}) + guid5 = call(cp, method='POST', document='document', content={'prop': 'f'}) self.utime('db/document/%s/%s' % (guid5[:2], guid5), 2) - in_seq = util.Sequence([[1, None]]) + in_seq = toolkit.Sequence([[1, None]]) patch = diff(volume, in_seq) - self.assertEqual({'document': 'document'}, patch.send(None)) + 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']) @@ -143,7 +143,7 @@ class VolumeTest(tests.Test): pass patch = diff(volume, in_seq) - self.assertEqual({'document': 'document'}, patch.send(None)) + 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']) @@ -156,29 +156,29 @@ class VolumeTest(tests.Test): def test_diff_DoNotStretchContinuesPacket(self): - class Document(db.Document): + class Document(db.Resource): @db.indexed_property(slot=1) def prop(self, value): return value - volume = Volume('db', [Document]) - cp = NodeCommands('guid', volume) + volume = db.Volume('db', [Document]) + cp = NodeRoutes('guid', volume) - guid1 = call(cp, method='POST', document='document', principal='principal', content={'prop': 'a'}) + guid1 = call(cp, method='POST', document='document', content={'prop': 'a'}) volume['document'].delete(guid1) - guid2 = call(cp, method='POST', document='document', principal='principal', content={'prop': 'b'}) + guid2 = call(cp, method='POST', document='document', content={'prop': 'b'}) volume['document'].delete(guid2) - guid3 = call(cp, method='POST', document='document', principal='principal', content={'prop': 'c'}) + 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', principal='principal', content={'prop': 'd'}) + guid4 = call(cp, method='POST', document='document', content={'prop': 'd'}) volume['document'].delete(guid4) - guid5 = call(cp, method='POST', document='document', principal='principal', content={'prop': 'f'}) + guid5 = call(cp, method='POST', document='document', content={'prop': 'f'}) self.utime('db/document/%s/%s' % (guid5[:2], guid5), 2) - in_seq = util.Sequence([[1, None]]) - patch = diff(volume, in_seq, util.Sequence([[1, 1]])) - self.assertEqual({'document': 'document'}, patch.send(None)) + 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)) @@ -190,32 +190,32 @@ class VolumeTest(tests.Test): def test_diff_TheSameInSeqForAllDocuments(self): - class Document1(db.Document): + class Document1(db.Resource): pass - class Document2(db.Document): + class Document2(db.Resource): pass - class Document3(db.Document): + class Document3(db.Resource): pass - volume = Volume('db', [Document1, Document2, Document3]) - cp = NodeCommands('guid', volume) + volume = db.Volume('db', [Document1, Document2, Document3]) + cp = NodeRoutes('guid', volume) - guid3 = call(cp, method='POST', document='document1', principal='principal', content={}) + 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', principal='principal', content={}) + 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', principal='principal', content={}) + guid1 = call(cp, method='POST', document='document3', content={}) self.utime('db/document/%s/%s' % (guid1[:2], guid1), 1) - in_seq = util.Sequence([[1, None]]) + in_seq = toolkit.Sequence([[1, None]]) patch = diff(volume, in_seq) - self.assertEqual({'document': 'document1'}, patch.send(None)) + self.assertEqual({'resource': 'document1'}, patch.send(None)) self.assertEqual(guid3, patch.send(None)['guid']) - self.assertEqual({'document': 'document2'}, patch.send(None)) + self.assertEqual({'resource': 'document2'}, patch.send(None)) self.assertEqual(guid2, patch.send(None)['guid']) - self.assertEqual({'document': 'document3'}, patch.send(None)) + self.assertEqual({'resource': 'document3'}, patch.send(None)) self.assertEqual(guid1, patch.send(None)['guid']) self.assertEqual({'commit': [[1, 3]]}, patch.send(None)) try: @@ -226,27 +226,27 @@ class VolumeTest(tests.Test): def test_merge_Create(self): - class Document1(db.Document): + class Document1(db.Resource): @db.indexed_property(slot=1) def prop(self, value): return value - class Document2(db.Document): + class Document2(db.Resource): pass self.touch(('db/seqno', '100')) - volume = Volume('db', [Document1, Document2]) + volume = db.Volume('db', [Document1, Document2]) records = [ - {'document': 'document1'}, + {'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}, }}, - {'document': 'document2'}, + {'resource': 'document2'}, {'guid': '5', 'diff': { 'guid': {'value': '5', 'mtime': 5.0}, 'ctime': {'value': 6, 'mtime': 6.0}, @@ -273,20 +273,20 @@ class VolumeTest(tests.Test): def test_merge_Update(self): - class Document(db.Document): + class Document(db.Resource): @db.indexed_property(slot=1) def prop(self, value): return value self.touch(('db/seqno', '100')) - volume = Volume('db', [Document]) + 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 = [ - {'document': 'document'}, + {'resource': 'document'}, {'guid': '1', 'diff': {'prop': {'value': '2', 'mtime': 1.0}}}, {'commit': [[1, 1]]}, ] @@ -297,7 +297,7 @@ class VolumeTest(tests.Test): self.assertEqual(2, os.stat('db/document/1/1/prop').st_mtime) records = [ - {'document': 'document'}, + {'resource': 'document'}, {'guid': '1', 'diff': {'prop': {'value': '3', 'mtime': 2.0}}}, {'commit': [[2, 2]]}, ] @@ -308,7 +308,7 @@ class VolumeTest(tests.Test): self.assertEqual(2, os.stat('db/document/1/1/prop').st_mtime) records = [ - {'document': 'document'}, + {'resource': 'document'}, {'guid': '1', 'diff': {'prop': {'value': '4', 'mtime': 3.0}}}, {'commit': [[3, 3]]}, ] @@ -320,15 +320,15 @@ class VolumeTest(tests.Test): def test_merge_MultipleCommits(self): - class Document(db.Document): + class Document(db.Resource): pass self.touch(('db/seqno', '100')) - volume = Volume('db', [Document]) + volume = db.Volume('db', [Document]) def generator(): for i in [ - {'document': 'document'}, + {'resource': 'document'}, {'commit': [[1, 1]]}, {'guid': '1', 'diff': { 'guid': {'value': '1', 'mtime': 1.0}, @@ -346,18 +346,18 @@ class VolumeTest(tests.Test): def test_merge_UpdateReviewStats(self): stats_node_step.value = 1 - volume = Volume('db', [User, Context, Review, Feedback, Solution, Artifact]) - cp = NodeCommands('guid', volume) + volume = db.Volume('db', model.RESOURCES) + cp = NodeRoutes('guid', volume) stats = Sniffer(volume) - context = call(cp, method='POST', document='context', principal='principal', content={ + context = call(cp, method='POST', document='context', content={ 'guid': 'context', 'type': 'package', 'title': 'title', 'summary': 'summary', 'description': 'description', }) - artifact = call(cp, method='POST', document='artifact', principal='principal', content={ + artifact = call(cp, method='POST', document='artifact', content={ 'guid': 'artifact', 'type': 'instance', 'context': context, @@ -366,7 +366,7 @@ class VolumeTest(tests.Test): }) records = [ - {'document': 'review'}, + {'resource': 'review'}, {'guid': '1', 'diff': { 'guid': {'value': '1', 'mtime': 1.0}, 'ctime': {'value': 1, 'mtime': 1.0}, @@ -374,6 +374,9 @@ class VolumeTest(tests.Test): 'context': {'value': context, 'mtime': 1.0}, 'artifact': {'value': artifact, 'mtime': 4.0}, 'rating': {'value': 1, 'mtime': 1.0}, + 'author': {'mtime': 1, 'value': {}}, + 'layer': {'mtime': 1, 'value': ['public']}, + 'tags': {'mtime': 1, 'value': []}, }}, {'guid': '2', 'diff': { 'guid': {'value': '2', 'mtime': 2.0}, @@ -381,6 +384,9 @@ class VolumeTest(tests.Test): 'mtime': {'value': 2, 'mtime': 2.0}, 'context': {'value': context, 'mtime': 2.0}, 'rating': {'value': 2, 'mtime': 2.0}, + 'author': {'mtime': 2, 'value': {}}, + 'layer': {'mtime': 2, 'value': ['public']}, + 'tags': {'mtime': 2, 'value': []}, }}, {'commit': [[1, 2]]}, ] @@ -394,22 +400,22 @@ class VolumeTest(tests.Test): def test_diff_Blobs(self): - class Document(Resource): + class Document(db.Resource): @db.blob_property() def prop(self, value): return value - volume = Volume('db', [User, Document]) - cp = NodeCommands('guid', volume) + volume = db.Volume('db', [Document]) + cp = NodeRoutes('guid', volume) - guid = call(cp, method='POST', document='document', principal='principal', content={}) - call(cp, method='PUT', document='document', guid=guid, principal='principal', content={'prop': 'payload'}) + 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, util.Sequence([[1, None]])) + patch = diff(volume, toolkit.Sequence([[1, None]])) self.assertEqual( - {'document': 'document'}, + {'resource': 'document'}, next(patch)) record = next(patch) self.assertEqual('payload', ''.join([i for i in record.pop('blob')])) @@ -426,7 +432,7 @@ class VolumeTest(tests.Test): self.assertEqual( {'guid': guid, 'diff': { 'guid': {'value': guid, 'mtime': 0}, - 'author': {'value': {'principal': {'order': 0, 'role': 2}}, 'mtime': 0}, + 'author': {'mtime': 0, 'value': {}}, 'layer': {'mtime': 0, 'value': ['public']}, 'tags': {'mtime': 0, 'value': []}, 'mtime': {'value': 0, 'mtime': 0}, @@ -434,9 +440,6 @@ class VolumeTest(tests.Test): }}, next(patch)) self.assertEqual( - {'document': 'user'}, - next(patch)) - self.assertEqual( {'commit': [[1, 2]]}, next(patch)) self.assertRaises(StopIteration, next, patch) @@ -445,25 +448,25 @@ class VolumeTest(tests.Test): url = 'http://src.sugarlabs.org/robots.txt' blob = urllib2.urlopen(url).read() - class Document(Resource): + class Document(db.Resource): @db.blob_property() def prop(self, value): return value - volume = Volume('db', [User, Document]) - cp = NodeCommands('guid', volume) + volume = db.Volume('db', [Document]) + cp = NodeRoutes('guid', volume) - guid = call(cp, method='POST', document='document', principal='principal', content={}) - call(cp, method='PUT', document='document', guid=guid, principal='principal', content={'prop': {'url': url}}) + 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([ - {'document': 'document'}, + {'resource': 'document'}, {'guid': guid, 'diff': { 'guid': {'value': guid, 'mtime': 1}, - 'author': {'value': {'principal': {'order': 0, 'role': 2}}, 'mtime': 1}, + 'author': {'mtime': 1, 'value': {}}, 'layer': {'mtime': 1, 'value': ['public']}, 'tags': {'mtime': 1, 'value': []}, 'mtime': {'value': 0, 'mtime': 1}, @@ -471,14 +474,13 @@ class VolumeTest(tests.Test): 'prop': {'url': url, 'mtime': 1}, }, }, - {'document': 'user'}, {'commit': [[1, 2]]}, ], - [i for i in diff(volume, util.Sequence([[1, None]]))]) + [i for i in diff(volume, toolkit.Sequence([[1, None]]))]) - patch = diff(volume, util.Sequence([[1, None]]), fetch_blobs=True) + patch = diff(volume, toolkit.Sequence([[1, None]]), fetch_blobs=True) self.assertEqual( - {'document': 'document'}, + {'resource': 'document'}, next(patch)) record = next(patch) self.assertEqual(blob, ''.join([i for i in record.pop('blob')])) @@ -488,7 +490,7 @@ class VolumeTest(tests.Test): self.assertEqual( {'guid': guid, 'diff': { 'guid': {'value': guid, 'mtime': 1}, - 'author': {'value': {'principal': {'order': 0, 'role': 2}}, 'mtime': 1}, + 'author': {'mtime': 1, 'value': {}}, 'layer': {'mtime': 1, 'value': ['public']}, 'tags': {'mtime': 1, 'value': []}, 'mtime': {'value': 0, 'mtime': 1}, @@ -496,35 +498,32 @@ class VolumeTest(tests.Test): }}, next(patch)) self.assertEqual( - {'document': 'user'}, - next(patch)) - self.assertEqual( {'commit': [[1, 2]]}, next(patch)) self.assertRaises(StopIteration, next, patch) def test_diff_SkipBrokenBlobUrls(self): - class Document(Resource): + class Document(db.Resource): @db.blob_property() def prop(self, value): return value - volume = Volume('db', [User, Document]) - cp = NodeCommands('guid', volume) + volume = db.Volume('db', [Document]) + cp = NodeRoutes('guid', volume) - guid1 = call(cp, method='POST', document='document', principal='principal', content={}) - call(cp, method='PUT', document='document', guid=guid1, principal='principal', content={'prop': {'url': 'http://foo/bar'}}) - guid2 = call(cp, method='POST', document='document', principal='principal', content={}) + 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([ - {'document': 'document'}, + {'resource': 'document'}, {'guid': guid1, 'diff': { 'guid': {'value': guid1, 'mtime': 1}, - 'author': {'value': {'principal': {'order': 0, 'role': 2}}, 'mtime': 1}, + 'author': {'mtime': 1, 'value': {}}, 'layer': {'mtime': 1, 'value': ['public']}, 'tags': {'mtime': 1, 'value': []}, 'mtime': {'value': 0, 'mtime': 1}, @@ -535,24 +534,23 @@ class VolumeTest(tests.Test): {'guid': guid2, 'diff': { 'guid': {'value': guid2, 'mtime': 1}, - 'author': {'value': {'principal': {'order': 0, 'role': 2}}, 'mtime': 1}, + 'author': {'mtime': 1, 'value': {}}, 'layer': {'mtime': 1, 'value': ['public']}, 'tags': {'mtime': 1, 'value': []}, 'mtime': {'value': 0, 'mtime': 1}, 'ctime': {'value': 0, 'mtime': 1}, }, }, - {'document': 'user'}, {'commit': [[1, 3]]}, ], - [i for i in diff(volume, util.Sequence([[1, None]]), fetch_blobs=False)]) + [i for i in diff(volume, toolkit.Sequence([[1, None]]), fetch_blobs=False)]) self.assertEqual([ - {'document': 'document'}, + {'resource': 'document'}, {'guid': guid1, 'diff': { 'guid': {'value': guid1, 'mtime': 1}, - 'author': {'value': {'principal': {'order': 0, 'role': 2}}, 'mtime': 1}, + 'author': {'mtime': 1, 'value': {}}, 'layer': {'mtime': 1, 'value': ['public']}, 'tags': {'mtime': 1, 'value': []}, 'mtime': {'value': 0, 'mtime': 1}, @@ -562,30 +560,29 @@ class VolumeTest(tests.Test): {'guid': guid2, 'diff': { 'guid': {'value': guid2, 'mtime': 1}, - 'author': {'value': {'principal': {'order': 0, 'role': 2}}, 'mtime': 1}, + 'author': {'mtime': 1, 'value': {}}, 'layer': {'mtime': 1, 'value': ['public']}, 'tags': {'mtime': 1, 'value': []}, 'mtime': {'value': 0, 'mtime': 1}, 'ctime': {'value': 0, 'mtime': 1}, }, }, - {'document': 'user'}, {'commit': [[1, 3]]}, ], - [i for i in diff(volume, util.Sequence([[1, None]]), fetch_blobs=True)]) + [i for i in diff(volume, toolkit.Sequence([[1, None]]), fetch_blobs=True)]) def test_merge_Blobs(self): - class Document(db.Document): + class Document(db.Resource): @db.blob_property() def prop(self, value): return value - volume = Volume('db', [Document]) + volume = db.Volume('db', [Document]) merge(volume, [ - {'document': 'document'}, + {'resource': 'document'}, {'guid': '1', 'diff': { 'guid': {'value': '1', 'mtime': 1.0}, 'ctime': {'value': 2, 'mtime': 2.0}, @@ -611,16 +608,16 @@ class VolumeTest(tests.Test): def test_diff_ByLayers(self): - class Context(Resource): + class Context(db.Resource): pass - class Implementation(Resource): + class Implementation(db.Resource): pass - class Review(Resource): + class Review(db.Resource): pass - volume = Volume('db', [Context, Implementation, Review]) + volume = db.Volume('db', [Context, Implementation, 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['implementation'].create({'guid': '2', 'ctime': 2, 'mtime': 2, 'layer': 'layer2'}) @@ -633,56 +630,65 @@ class VolumeTest(tests.Test): self.utime('db', 0) self.assertEqual(sorted([ - {'document': 'context'}, + {'resource': 'context'}, {'guid': '0', 'diff': {'tags': {'value': '0', 'mtime': 0}}}, {'guid': '1', 'diff': {'tags': {'value': '1', 'mtime': 0}}}, - {'document': 'implementation'}, + {'resource': 'implementation'}, {'guid': '2', 'diff': {'tags': {'value': '2', 'mtime': 0}}}, - {'document': 'review'}, + {'resource': 'review'}, {'guid': '3', 'diff': {'tags': {'value': '3', 'mtime': 0}}}, {'commit': [[5, 8]]}, ]), - sorted([i for i in diff(volume, util.Sequence([[5, None]]))])) + sorted([i for i in diff(volume, toolkit.Sequence([[5, None]]))])) self.assertEqual(sorted([ - {'document': 'context'}, + {'resource': 'context'}, {'guid': '0', 'diff': {'tags': {'value': '0', 'mtime': 0}}}, {'guid': '1', 'diff': {'tags': {'value': '1', 'mtime': 0}}}, - {'document': 'implementation'}, - {'document': 'review'}, + {'resource': 'implementation'}, + {'resource': 'review'}, {'guid': '3', 'diff': {'tags': {'value': '3', 'mtime': 0}}}, {'commit': [[5, 8]]}, ]), - sorted([i for i in diff(volume, util.Sequence([[5, None]]), layer='layer1')])) + sorted([i for i in diff(volume, toolkit.Sequence([[5, None]]), layer='layer1')])) self.assertEqual(sorted([ - {'document': 'context'}, + {'resource': 'context'}, {'guid': '0', 'diff': {'tags': {'value': '0', 'mtime': 0}}}, - {'document': 'implementation'}, + {'resource': 'implementation'}, {'guid': '2', 'diff': {'tags': {'value': '2', 'mtime': 0}}}, - {'document': 'review'}, + {'resource': 'review'}, {'guid': '3', 'diff': {'tags': {'value': '3', 'mtime': 0}}}, {'commit': [[5, 8]]}, ]), - sorted([i for i in diff(volume, util.Sequence([[5, None]]), layer='layer2')])) + sorted([i for i in diff(volume, toolkit.Sequence([[5, None]]), layer='layer2')])) self.assertEqual(sorted([ - {'document': 'context'}, + {'resource': 'context'}, {'guid': '0', 'diff': {'tags': {'value': '0', 'mtime': 0}}}, - {'document': 'implementation'}, - {'document': 'review'}, + {'resource': 'implementation'}, + {'resource': 'review'}, {'guid': '3', 'diff': {'tags': {'value': '3', 'mtime': 0}}}, {'commit': [[5, 8]]}, ]), - sorted([i for i in diff(volume, util.Sequence([[5, None]]), layer='foo')])) - - -def call(cp, principal=None, content=None, **kwargs): - request = db.Request(**kwargs) - request.principal = principal + 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) + request.update(kwargs) + request.cmd = cmd request.content = content request.environ = {'HTTP_HOST': '127.0.0.1'} - return cp.call(request, db.Response()) + router = Router(routes) + return router.call(request, Response()) if __name__ == '__main__': diff --git a/tests/units/resources/volume.py b/tests/units/resources/volume.py deleted file mode 100755 index 687f9c8..0000000 --- a/tests/units/resources/volume.py +++ /dev/null @@ -1,444 +0,0 @@ -#!/usr/bin/env python -# sugar-lint: disable - -import os -import json -import time -from os.path import exists - -from __init__ import tests - -from sugar_network import db, node -from sugar_network.resources.volume import Volume, Resource, Commands -from sugar_network.resources.user import User -from sugar_network.toolkit import coroutine, util - - -class VolumeTest(tests.Test): - - def test_Subscribe(self): - - class Document(Resource): - - @db.indexed_property(slot=1) - def prop(self, value): - return value - - volume = Volume('db', [Document]) - cp = TestCommands(volume) - events = [] - - def read_events(): - for event in cp.subscribe(event='!commit'): - if not event.strip(): - continue - assert event.startswith('data: ') - assert event.endswith('\n\n') - event = json.loads(event[6:]) - events.append(event) - - job = coroutine.spawn(read_events) - coroutine.dispatch() - volume['document'].create({'guid': 'guid', 'prop': 'value1'}) - coroutine.dispatch() - volume['document'].update('guid', {'prop': 'value2'}) - coroutine.dispatch() - volume['document'].delete('guid') - coroutine.dispatch() - volume['document'].commit() - coroutine.sleep(.5) - job.kill() - - self.assertEqual([ - {'guid': 'guid', 'document': 'document', 'event': 'create'}, - {'guid': 'guid', 'document': 'document', 'event': 'update'}, - {'guid': 'guid', 'event': 'delete', 'document': u'document'}, - ], - events) - - def test_SubscribeWithPong(self): - volume = Volume('db', []) - cp = TestCommands(volume) - - for event in cp.subscribe(ping=True): - break - self.assertEqual('data: {"event": "pong"}\n\n', event) - - def __test_SubscribeCondition(self): - - class Document(Resource): - - @db.indexed_property(slot=1) - def prop(self, value): - return value - - volume = Volume('db', [Document]) - cp = TestCommands(volume) - events = [] - - def read_events(): - for event in cp.subscribe(db.Request(), db.Response(), only_commits=True): - if not event.strip(): - continue - assert event.startswith('data: ') - assert event.endswith('\n\n') - event = json.loads(event[6:]) - events.append(event) - - job = coroutine.spawn(read_events) - coroutine.dispatch() - volume['document'].create({'guid': 'guid', 'prop': 'value1'}) - coroutine.dispatch() - volume['document'].update('guid', {'prop': 'value2'}) - coroutine.dispatch() - volume['document'].delete('guid') - coroutine.dispatch() - volume['document'].commit() - coroutine.sleep(.5) - job.kill() - - self.assertEqual([ - {'document': 'document', 'event': 'commit'}, - {'document': 'document', 'event': 'commit'}, - {'document': 'document', 'event': 'commit'}, - ], - events) - - def test_Populate(self): - self.touch( - ('db/context/1/1/guid', json.dumps({"value": "1"})), - ('db/context/1/1/ctime', json.dumps({"value": 1})), - ('db/context/1/1/mtime', json.dumps({"value": 1})), - ('db/context/1/1/seqno', json.dumps({"value": 0})), - ('db/context/1/1/type', json.dumps({"value": "activity"})), - ('db/context/1/1/title', json.dumps({"value": {}})), - ('db/context/1/1/summary', json.dumps({"value": {}})), - ('db/context/1/1/description', json.dumps({"value": {}})), - ) - - volume = Volume('db') - cp = TestCommands(volume) - assert exists('db/context/index') - - def test_DefaultAuthor(self): - - class Document(Resource): - pass - - volume = Volume('db', [User, Document]) - cp = TestCommands(volume) - - guid = call(cp, method='POST', document='document', content={}, principal='user') - self.assertEqual( - [{'name': 'user', 'role': 2}], - call(cp, method='GET', document='document', guid=guid, prop='author')) - self.assertEqual( - {'user': {'role': 2, 'order': 0}}, - volume['document'].get(guid)['author']) - - volume['user'].create({'guid': 'user', 'color': '', 'pubkey': '', 'name': 'User'}) - - guid = call(cp, method='POST', document='document', content={}, principal='user') - self.assertEqual( - [{'guid': 'user', 'name': 'User', 'role': 3}], - call(cp, method='GET', document='document', guid=guid, prop='author')) - self.assertEqual( - {'user': {'name': 'User', 'role': 3, 'order': 0}}, - volume['document'].get(guid)['author']) - - def test_FindByAuthor(self): - - class Document(Resource): - pass - - volume = Volume('db', [User, Document]) - cp = TestCommands(volume) - - volume['user'].create({'guid': 'user1', 'color': '', 'pubkey': '', 'name': 'UserName1'}) - volume['user'].create({'guid': 'user2', 'color': '', 'pubkey': '', 'name': 'User Name2'}) - volume['user'].create({'guid': 'user3', 'color': '', 'pubkey': '', 'name': 'User Name 3'}) - - guid1 = call(cp, method='POST', document='document', content={}, principal='user1') - guid2 = call(cp, method='POST', document='document', content={}, principal='user2') - guid3 = call(cp, method='POST', document='document', content={}, principal='user3') - - self.assertEqual(sorted([ - {'guid': guid1}, - ]), - call(cp, method='GET', document='document', author='UserName1')['result']) - - self.assertEqual(sorted([ - {'guid': guid1}, - ]), - sorted(call(cp, method='GET', document='document', query='author:UserName')['result'])) - self.assertEqual(sorted([ - {'guid': guid1}, - {'guid': guid2}, - {'guid': guid3}, - ]), - sorted(call(cp, method='GET', document='document', query='author:User')['result'])) - self.assertEqual(sorted([ - {'guid': guid2}, - {'guid': guid3}, - ]), - sorted(call(cp, method='GET', document='document', query='author:Name')['result'])) - - def test_PreserveAuthorsOrder(self): - - class Document(Resource): - pass - - volume = Volume('db', [User, Document]) - cp = TestCommands(volume) - - volume['user'].create({'guid': 'user1', 'color': '', 'pubkey': '', 'name': 'User1'}) - volume['user'].create({'guid': 'user2', 'color': '', 'pubkey': '', 'name': 'User2'}) - volume['user'].create({'guid': 'user3', 'color': '', 'pubkey': '', 'name': 'User3'}) - - guid = call(cp, method='POST', document='document', content={}, principal='user1') - call(cp, method='PUT', document='document', guid=guid, cmd='useradd', user='user2', role=0) - call(cp, method='PUT', document='document', guid=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}, - ], - call(cp, method='GET', document='document', guid=guid, prop='author')) - self.assertEqual({ - 'user1': {'name': 'User1', 'role': 3, 'order': 0}, - 'user2': {'name': 'User2', 'role': 1, 'order': 1}, - 'user3': {'name': 'User3', 'role': 1, 'order': 2}, - }, - volume['document'].get(guid)['author']) - - call(cp, method='PUT', document='document', guid=guid, cmd='userdel', user='user2', principal='user1') - call(cp, method='PUT', document='document', guid=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}, - ], - call(cp, method='GET', document='document', guid=guid, prop='author')) - self.assertEqual({ - 'user1': {'name': 'User1', 'role': 3, 'order': 0}, - 'user3': {'name': 'User3', 'role': 1, 'order': 2}, - 'user2': {'name': 'User2', 'role': 1, 'order': 3}, - }, - volume['document'].get(guid)['author']) - - call(cp, method='PUT', document='document', guid=guid, cmd='userdel', user='user2', principal='user1') - call(cp, method='PUT', document='document', guid=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}, - ], - call(cp, method='GET', document='document', guid=guid, prop='author')) - self.assertEqual({ - 'user1': {'name': 'User1', 'role': 3, 'order': 0}, - 'user3': {'name': 'User3', 'role': 1, 'order': 2}, - 'user2': {'name': 'User2', 'role': 1, 'order': 3}, - }, - volume['document'].get(guid)['author']) - - call(cp, method='PUT', document='document', guid=guid, cmd='userdel', user='user3', principal='user1') - call(cp, method='PUT', document='document', guid=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}, - ], - call(cp, method='GET', document='document', guid=guid, prop='author')) - self.assertEqual({ - 'user1': {'name': 'User1', 'role': 3, 'order': 0}, - 'user2': {'name': 'User2', 'role': 1, 'order': 3}, - 'user3': {'name': 'User3', 'role': 1, 'order': 4}, - }, - volume['document'].get(guid)['author']) - - def test_AddUser(self): - - class Document(Resource): - pass - - volume = Volume('db', [User, Document]) - cp = TestCommands(volume) - - volume['user'].create({'guid': 'user1', 'color': '', 'pubkey': '', 'name': 'User1'}) - volume['user'].create({'guid': 'user2', 'color': '', 'pubkey': '', 'name': 'User2'}) - - guid = call(cp, method='POST', document='document', content={}, principal='user1') - self.assertEqual([ - {'guid': 'user1', 'name': 'User1', 'role': 3}, - ], - call(cp, method='GET', document='document', guid=guid, prop='author')) - self.assertEqual({ - 'user1': {'name': 'User1', 'role': 3, 'order': 0}, - }, - volume['document'].get(guid)['author']) - - call(cp, method='PUT', document='document', guid=guid, cmd='useradd', user='user2', role=2) - self.assertEqual([ - {'guid': 'user1', 'name': 'User1', 'role': 3}, - {'guid': 'user2', 'name': 'User2', 'role': 3}, - ], - call(cp, method='GET', document='document', guid=guid, prop='author')) - self.assertEqual({ - 'user1': {'name': 'User1', 'role': 3, 'order': 0}, - 'user2': {'name': 'User2', 'role': 3, 'order': 1}, - }, - volume['document'].get(guid)['author']) - - call(cp, method='PUT', document='document', guid=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}, - ], - call(cp, method='GET', document='document', guid=guid, prop='author')) - self.assertEqual({ - 'user1': {'name': 'User1', 'role': 3, 'order': 0}, - 'user2': {'name': 'User2', 'role': 3, 'order': 1}, - 'User3': {'role': 2, 'order': 2}, - }, - volume['document'].get(guid)['author']) - - call(cp, method='PUT', document='document', guid=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}, - ], - call(cp, method='GET', document='document', guid=guid, prop='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}, - }, - volume['document'].get(guid)['author']) - - def test_UpdateAuthor(self): - - class Document(Resource): - pass - - volume = Volume('db', [User, Document]) - cp = TestCommands(volume) - - volume['user'].create({'guid': 'user1', 'color': '', 'pubkey': '', 'name': 'User1'}) - guid = call(cp, method='POST', document='document', content={}, principal='user1') - - call(cp, method='PUT', document='document', guid=guid, cmd='useradd', user='User2', role=0) - self.assertEqual([ - {'guid': 'user1', 'name': 'User1', 'role': 3}, - {'name': 'User2', 'role': 0}, - ], - call(cp, method='GET', document='document', guid=guid, prop='author')) - self.assertEqual({ - 'user1': {'name': 'User1', 'role': 3, 'order': 0}, - 'User2': {'role': 0, 'order': 1}, - }, - volume['document'].get(guid)['author']) - - call(cp, method='PUT', document='document', guid=guid, cmd='useradd', user='user1', role=0) - self.assertEqual([ - {'guid': 'user1', 'name': 'User1', 'role': 1}, - {'name': 'User2', 'role': 0}, - ], - call(cp, method='GET', document='document', guid=guid, prop='author')) - self.assertEqual({ - 'user1': {'name': 'User1', 'role': 1, 'order': 0}, - 'User2': {'role': 0, 'order': 1}, - }, - volume['document'].get(guid)['author']) - - call(cp, method='PUT', document='document', guid=guid, cmd='useradd', user='User2', role=2) - self.assertEqual([ - {'guid': 'user1', 'name': 'User1', 'role': 1}, - {'name': 'User2', 'role': 2}, - ], - call(cp, method='GET', document='document', guid=guid, prop='author')) - self.assertEqual({ - 'user1': {'name': 'User1', 'role': 1, 'order': 0}, - 'User2': {'role': 2, 'order': 1}, - }, - volume['document'].get(guid)['author']) - - def test_DelUser(self): - - class Document(Resource): - pass - - volume = Volume('db', [User, Document]) - cp = TestCommands(volume) - - volume['user'].create({'guid': 'user1', 'color': '', 'pubkey': '', 'name': 'User1'}) - volume['user'].create({'guid': 'user2', 'color': '', 'pubkey': '', 'name': 'User2'}) - guid = call(cp, method='POST', document='document', content={}, principal='user1') - call(cp, method='PUT', document='document', guid=guid, cmd='useradd', user='user2') - call(cp, method='PUT', document='document', guid=guid, cmd='useradd', user='User3') - self.assertEqual([ - {'guid': 'user1', 'name': 'User1', 'role': 3}, - {'guid': 'user2', 'name': 'User2', 'role': 1}, - {'name': 'User3', 'role': 0}, - ], - call(cp, method='GET', document='document', guid=guid, prop='author')) - self.assertEqual({ - 'user1': {'name': 'User1', 'role': 3, 'order': 0}, - 'user2': {'name': 'User2', 'role': 1, 'order': 1}, - 'User3': {'role': 0, 'order': 2}, - }, - volume['document'].get(guid)['author']) - - # Do not remove yourself - self.assertRaises(RuntimeError, call, cp, method='PUT', document='document', guid=guid, cmd='userdel', user='user1', principal='user1') - self.assertRaises(RuntimeError, call, cp, method='PUT', document='document', guid=guid, cmd='userdel', user='user2', principal='user2') - - call(cp, method='PUT', document='document', guid=guid, cmd='userdel', user='user1', principal='user2') - self.assertEqual([ - {'guid': 'user2', 'name': 'User2', 'role': 1}, - {'name': 'User3', 'role': 0}, - ], - call(cp, method='GET', document='document', guid=guid, prop='author')) - self.assertEqual({ - 'user2': {'name': 'User2', 'role': 1, 'order': 1}, - 'User3': {'role': 0, 'order': 2}, - }, - volume['document'].get(guid)['author']) - - call(cp, method='PUT', document='document', guid=guid, cmd='userdel', user='User3', principal='user2') - self.assertEqual([ - {'guid': 'user2', 'name': 'User2', 'role': 1}, - ], - call(cp, method='GET', document='document', guid=guid, prop='author')) - self.assertEqual({ - 'user2': {'name': 'User2', 'role': 1, 'order': 1}, - }, - volume['document'].get(guid)['author']) - - -class TestCommands(db.VolumeCommands, Commands): - - def __init__(self, volume): - db.VolumeCommands.__init__(self, volume) - Commands.__init__(self) - self.volume.connect(self.broadcast) - - -def call(cp, principal=None, content=None, **kwargs): - request = db.Request(**kwargs) - request.principal = principal - request.content = content - request.environ = {'HTTP_HOST': '127.0.0.1'} - request.commands = cp - return cp.call(request, db.Response()) - - -if __name__ == '__main__': - tests.main() diff --git a/tests/units/toolkit/__main__.py b/tests/units/toolkit/__main__.py index 9871e6b..841711e 100644 --- a/tests/units/toolkit/__main__.py +++ b/tests/units/toolkit/__main__.py @@ -6,9 +6,10 @@ from http import * from lsb_release import * from mountpoints import * from rrd import * -from util import * +from toolkit import * from options import * from spec import * +from router import * if __name__ == '__main__': tests.main() diff --git a/tests/units/toolkit/http.py b/tests/units/toolkit/http.py index f1fe10e..4117cbc 100755 --- a/tests/units/toolkit/http.py +++ b/tests/units/toolkit/http.py @@ -6,8 +6,8 @@ import select from __init__ import tests -from sugar_network import db, client as local -from sugar_network.db import router +from sugar_network import client as local +from sugar_network.toolkit.router import route, Router, Request from sugar_network.toolkit import coroutine, http @@ -15,18 +15,17 @@ class HTTPTest(tests.Test): def test_Subscribe(self): - class CommandsProcessor(db.CommandsProcessor): + class CommandsProcessor(object): events = [] - @router.route('GET', '/') + @route('GET', cmd='subscribe') def subscribe(self, request, response): - assert request.get('cmd') == 'subscribe' while CommandsProcessor.events: - coroutine.sleep(.3) + coroutine.sleep(.1) yield CommandsProcessor.events.pop(0) + '\n' - self.server = coroutine.WSGIServer(('127.0.0.1', local.ipc_port.value), router.Router(CommandsProcessor())) + self.server = coroutine.WSGIServer(('127.0.0.1', local.ipc_port.value), Router(CommandsProcessor())) coroutine.spawn(self.server.serve_forever) coroutine.dispatch() client = http.Client('http://127.0.0.1:%s' % local.ipc_port.value) @@ -57,29 +56,33 @@ class HTTPTest(tests.Test): def test_call_ReturnStream(self): - class Commands(db.CommandsProcessor): + class Commands(object): - @db.volume_command(method='GET', cmd='f1', mime_type='application/json') + @route('GET', cmd='f1', mime_type='application/json') def f1(self): yield json.dumps('result') - @db.volume_command(method='GET', cmd='f2', mime_type='foo/bar') + @route('GET', cmd='f2', mime_type='foo/bar') def f2(self): yield json.dumps('result') - self.server = coroutine.WSGIServer(('127.0.0.1', local.ipc_port.value), router.Router(Commands())) + self.server = coroutine.WSGIServer(('127.0.0.1', local.ipc_port.value), Router(Commands())) coroutine.spawn(self.server.serve_forever) coroutine.dispatch() client = http.Client('http://127.0.0.1:%s' % local.ipc_port.value) - request = db.Request() - request['method'] = 'GET' - request['cmd'] = 'f1' + request = Request({ + 'REQUEST_METHOD': 'GET', + 'PATH_INFO': '/', + 'QUERY_STRING': 'cmd=f1', + }) self.assertEqual('result', client.call(request)) - request = db.Request() - request['method'] = 'GET' - request['cmd'] = 'f2' + request = Request({ + 'REQUEST_METHOD': 'GET', + 'PATH_INFO': '/', + 'QUERY_STRING': 'cmd=f2', + }) self.assertEqual('result', json.load(client.call(request))) diff --git a/tests/units/toolkit/router.py b/tests/units/toolkit/router.py new file mode 100755 index 0000000..32e26f3 --- /dev/null +++ b/tests/units/toolkit/router.py @@ -0,0 +1,1257 @@ +#!/usr/bin/env python +# sugar-lint: disable + +import os +import json +from email.utils import formatdate +from cStringIO import StringIO + +from __init__ import tests, src_root + +from sugar_network import db +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 + + +class RouterTest(tests.Test): + + def test_routes_Exact(self): + + class Routes(object): + + @route('PROBE') + def command_1(self): + return 'command_1' + + @route('PROBE', cmd='command_2') + def command_2(self): + return 'command_2' + + @route('PROBE', ['resource']) + def command_3(self): + return 'command_3' + + @route('PROBE', ['resource'], cmd='command_4') + def command_4(self): + return 'command_4' + + @route('PROBE', ['resource', 'guid']) + def command_5(self): + return 'command_5' + + @route('PROBE', ['resource', 'guid'], cmd='command_6') + def command_6(self): + return 'command_6' + + @route('PROBE', ['resource', 'guid', 'prop']) + def command_7(self): + return 'command_7' + + @route('PROBE', ['resource', 'guid', 'prop'], cmd='command_8') + def command_8(self): + return 'command_8' + + router = Router(Routes()) + status = [] + + self.assertEqual( + ['command_1'], + [i for i in router({ + 'REQUEST_METHOD': 'PROBE', + 'PATH_INFO': '/', + 'QUERY_STRING': '', + }, lambda *args: status.append(args))]) + self.assertEqual(('200 OK', [('content-length', str(len('command_1')))]), status[-1]) + + self.assertEqual( + ['command_2'], + [i for i in router({ + 'REQUEST_METHOD': 'PROBE', + 'PATH_INFO': '/', + 'QUERY_STRING': 'cmd=command_2', + }, lambda *args: status.append(args))]) + self.assertEqual(('200 OK', [('content-length', str(len('command_2')))]), status[-1]) + + self.assertEqual( + ['command_3'], + [i for i in router({ + 'REQUEST_METHOD': 'PROBE', + 'PATH_INFO': '/resource', + 'QUERY_STRING': '', + }, lambda *args: status.append(args))]) + self.assertEqual(('200 OK', [('content-length', str(len('command_3')))]), status[-1]) + + self.assertEqual( + ['command_4'], + [i for i in router({ + 'REQUEST_METHOD': 'PROBE', + 'PATH_INFO': '/resource', + 'QUERY_STRING': 'cmd=command_4', + }, lambda *args: status.append(args))]) + self.assertEqual(('200 OK', [('content-length', str(len('command_4')))]), status[-1]) + + self.assertEqual( + ['command_5'], + [i for i in router({ + 'REQUEST_METHOD': 'PROBE', + 'PATH_INFO': '/resource/guid', + 'QUERY_STRING': '', + }, lambda *args: status.append(args))]) + self.assertEqual(('200 OK', [('content-length', str(len('command_5')))]), status[-1]) + + self.assertEqual( + ['command_6'], + [i for i in router({ + 'REQUEST_METHOD': 'PROBE', + 'PATH_INFO': '/resource/guid', + 'QUERY_STRING': 'cmd=command_6', + }, lambda *args: status.append(args))]) + self.assertEqual(('200 OK', [('content-length', str(len('command_6')))]), status[-1]) + + self.assertEqual( + ['command_7'], + [i for i in router({ + 'REQUEST_METHOD': 'PROBE', + 'PATH_INFO': '/resource/guid/prop', + 'QUERY_STRING': '', + }, lambda *args: status.append(args))]) + self.assertEqual(('200 OK', [('content-length', str(len('command_7')))]), status[-1]) + + self.assertEqual( + ['command_8'], + [i for i in router({ + 'REQUEST_METHOD': 'PROBE', + 'PATH_INFO': '/resource/guid/prop', + 'QUERY_STRING': 'cmd=command_8', + }, lambda *args: status.append(args))]) + self.assertEqual(('200 OK', [('content-length', str(len('command_8')))]), status[-1]) + + self.assertEqual( + ['{"request": "/*/*/*", "error": "Path not found"}'], + [i for i in router({'REQUEST_METHOD': 'PROBE', 'PATH_INFO': '/*/*/*'}, lambda *args: None)]) + self.assertEqual( + ['{"request": "/*", "error": "Path not found"}'], + [i for i in router({'REQUEST_METHOD': 'PROBE', 'PATH_INFO': '/*'}, lambda *args: None)]) + self.assertEqual( + ['{"request": "/?cmd=*", "error": "No such operation"}'], + [i for i in router({'REQUEST_METHOD': 'PROBE', 'PATH_INFO': '/', 'QUERY_STRING': 'cmd=*'}, lambda *args: None)]) + self.assertEqual( + ['{"request": "/", "error": "No such operation"}'], + [i for i in router({'REQUEST_METHOD': 'GET', 'PATH_INFO': '/'}, lambda *args: None)]) + + def test_routes_TailWildcards(self): + + class Routes(object): + + @route('PROBE', ['resource', 'guid', None]) + def command_1(self): + return 'command_1' + + @route('PROBE', ['resource', 'guid', None], cmd='command_2') + def command_2(self): + return 'command_2' + + @route('PROBE', ['resource', None, None]) + def command_3(self): + return 'command_3' + + @route('PROBE', ['resource', None, None], cmd='command_4') + def command_4(self): + return 'command_4' + + @route('PROBE', [None, None, None]) + def command_5(self): + return 'command_5' + + @route('PROBE', [None, None, None], cmd='command_6') + def command_6(self): + return 'command_6' + + router = Router(Routes()) + status = [] + + self.assertEqual( + ['command_1'], + [i for i in router({ + 'REQUEST_METHOD': 'PROBE', + 'PATH_INFO': '/resource/guid/*', + 'QUERY_STRING': '', + }, lambda *args: status.append(args))]) + self.assertEqual(('200 OK', [('content-length', str(len('command_1')))]), status[-1]) + + self.assertEqual( + ['command_2'], + [i for i in router({ + 'REQUEST_METHOD': 'PROBE', + 'PATH_INFO': '/resource/guid/*', + 'QUERY_STRING': 'cmd=command_2', + }, lambda *args: status.append(args))]) + self.assertEqual(('200 OK', [('content-length', str(len('command_2')))]), status[-1]) + + self.assertEqual( + ['command_3'], + [i for i in router({ + 'REQUEST_METHOD': 'PROBE', + 'PATH_INFO': '/resource/guid2/prop', + 'QUERY_STRING': '', + }, lambda *args: status.append(args))]) + self.assertEqual(('200 OK', [('content-length', str(len('command_3')))]), status[-1]) + + self.assertEqual( + ['command_4'], + [i for i in router({ + 'REQUEST_METHOD': 'PROBE', + 'PATH_INFO': '/resource/guid2/prop', + 'QUERY_STRING': 'cmd=command_4', + }, lambda *args: status.append(args))]) + self.assertEqual(('200 OK', [('content-length', str(len('command_4')))]), status[-1]) + + self.assertEqual( + ['command_5'], + [i for i in router({ + 'REQUEST_METHOD': 'PROBE', + 'PATH_INFO': '/*/guid/prop', + 'QUERY_STRING': '', + }, lambda *args: status.append(args))]) + self.assertEqual(('200 OK', [('content-length', str(len('command_5')))]), status[-1]) + + self.assertEqual( + ['command_6'], + [i for i in router({ + 'REQUEST_METHOD': 'PROBE', + 'PATH_INFO': '/*/guid/prop', + 'QUERY_STRING': 'cmd=command_6', + }, lambda *args: status.append(args))]) + self.assertEqual(('200 OK', [('content-length', str(len('command_6')))]), status[-1]) + + self.assertEqual( + ['{"request": "/", "error": "Path not found"}'], + [i for i in router({'REQUEST_METHOD': 'PROBE', 'PATH_INFO': '/'}, lambda *args: None)]) + self.assertEqual( + ['{"request": "/*/*/*?cmd=*", "error": "No such operation"}'], + [i for i in router({'REQUEST_METHOD': 'PROBE', 'PATH_INFO': '/*/*/*', 'QUERY_STRING': 'cmd=*'}, lambda *args: None)]) + self.assertEqual( + ['{"request": "/*/*/*", "error": "No such operation"}'], + [i for i in router({'REQUEST_METHOD': 'GET', 'PATH_INFO': '/*/*/*'}, lambda *args: None)]) + + def test_routes_FreeWildcards(self): + + class Routes(object): + + @route('PROBE', ['resource1', None, 'prop1']) + def command_1(self): + return 'command_1' + + @route('PROBE', ['resource1', None, 'prop1'], cmd='command_2') + def command_2(self): + return 'command_2' + + @route('PROBE', [None, None, 'prop2']) + def command_3(self): + return 'command_3' + + @route('PROBE', [None, None, 'prop2'], cmd='command_4') + def command_4(self): + return 'command_4' + + router = Router(Routes()) + status = [] + + self.assertEqual( + ['command_1'], + [i for i in router({ + 'REQUEST_METHOD': 'PROBE', + 'PATH_INFO': '/resource1/*/prop1', + 'QUERY_STRING': '', + }, lambda *args: status.append(args))]) + self.assertEqual(('200 OK', [('content-length', str(len('command_1')))]), status[-1]) + + self.assertEqual( + ['command_2'], + [i for i in router({ + 'REQUEST_METHOD': 'PROBE', + 'PATH_INFO': '/resource1/*/prop1', + 'QUERY_STRING': 'cmd=command_2', + }, lambda *args: status.append(args))]) + self.assertEqual(('200 OK', [('content-length', str(len('command_2')))]), status[-1]) + + self.assertEqual( + ['command_3'], + [i for i in router({ + 'REQUEST_METHOD': 'PROBE', + 'PATH_INFO': '/*/*/prop2', + 'QUERY_STRING': '', + }, lambda *args: status.append(args))]) + self.assertEqual(('200 OK', [('content-length', str(len('command_3')))]), status[-1]) + + self.assertEqual( + ['command_4'], + [i for i in router({ + 'REQUEST_METHOD': 'PROBE', + 'PATH_INFO': '/*/*/prop2', + 'QUERY_STRING': 'cmd=command_4', + }, lambda *args: status.append(args))]) + self.assertEqual(('200 OK', [('content-length', str(len('command_4')))]), status[-1]) + + self.assertEqual( + ['{"request": "/*/*/prop3", "error": "Path not found"}'], + [i for i in router({'REQUEST_METHOD': 'PROBE', 'PATH_INFO': '/*/*/prop3'}, lambda *args: None)]) + self.assertEqual( + ['{"request": "/*/*/prop2", "error": "No such operation"}'], + [i for i in router({'REQUEST_METHOD': 'GET', 'PATH_INFO': '/*/*/prop2'}, lambda *args: None)]) + + self.assertEqual( + ['{"request": "/", "error": "Path not found"}'], + [i for i in router({'REQUEST_METHOD': 'PROBE', 'PATH_INFO': '/'}, lambda *args: None)]) + self.assertEqual( + ['{"request": "/*/*/*?cmd=*", "error": "Path not found"}'], + [i for i in router({'REQUEST_METHOD': 'PROBE', 'PATH_INFO': '/*/*/*', 'QUERY_STRING': 'cmd=*'}, lambda *args: None)]) + self.assertEqual( + ['{"request": "/*/*/prop2", "error": "No such operation"}'], + [i for i in router({'REQUEST_METHOD': 'GET', 'PATH_INFO': '/*/*/prop2'}, lambda *args: None)]) + + def test_routes_Fallback(self): + + class Routes(object): + + @fallbackroute() + def fallback(self): + return 'fallback' + + @fallbackroute('PROBE2') + def fallback2(self): + return 'fallback2' + + @route('PROBE', ['exists']) + def exists(self): + return 'exists' + + router = Router(Routes()) + status = [] + + self.assertEqual( + ['exists'], + [i for i in router({'REQUEST_METHOD': 'PROBE', 'PATH_INFO': '/exists'}, lambda *args: None)]) + self.assertEqual( + ['fallback'], + [i for i in router({'REQUEST_METHOD': 'PUT', 'PATH_INFO': '/'}, lambda *args: None)]) + self.assertEqual( + ['fallback'], + [i for i in router({'REQUEST_METHOD': 'PROBE', 'PATH_INFO': '/*'}, lambda *args: None)]) + self.assertEqual( + ['fallback'], + [i for i in router({'REQUEST_METHOD': 'GET', 'PATH_INFO': '/*/*/*'}, lambda *args: None)]) + + self.assertEqual( + ['fallback2'], + [i for i in router({'REQUEST_METHOD': 'PROBE2', 'PATH_INFO': '/'}, lambda *args: None)]) + self.assertEqual( + ['fallback2'], + [i for i in router({'REQUEST_METHOD': 'PROBE2', 'PATH_INFO': '/*/*/*'}, lambda *args: None)]) + self.assertEqual( + ['fallback'], + [i for i in router({'REQUEST_METHOD': 'PROBE3', 'PATH_INFO': '/*/*/*/*/*'}, lambda *args: None)]) + + def test_routes_FallbackForCommands(self): + + class Routes(object): + + @fallbackroute() + def fallback(self): + return 'fallback' + + @fallbackroute('PROBE1', ['raise', 'fail']) + def fallback2(self): + return 'fallback2' + + @route('PROBE2', [None, None]) + def exists(self): + return 'exists' + + @route('PROBE3', [None, None], cmd='CMD') + def exists2(self): + return 'exists2' + + router = Router(Routes()) + + self.assertEqual( + ['fallback'], + [i for i in router({'REQUEST_METHOD': 'PROBE3', 'PATH_INFO': '/raise/fail', 'QUERY_STRING': 'cmd=FOO'}, lambda *args: None)]) + + def test_routes_FallbackAndRegularRouteOnTheSameLevel(self): + + class Routes(object): + + @fallbackroute() + def fallback(self): + return 'fallback' + + @route('PROBE') + def exists(self): + return 'exists' + + router = Router(Routes()) + status = [] + + self.assertEqual( + ['exists'], + [i for i in router({'REQUEST_METHOD': 'PROBE', 'PATH_INFO': '/'}, lambda *args: None)]) + self.assertEqual( + ['fallback'], + [i for i in router({'REQUEST_METHOD': 'GET', 'PATH_INFO': '/'}, lambda *args: None)]) + + def test_routes_CheckFallbacksBeforeWildecards(self): + + class Routes(object): + + @fallbackroute('PROBE', ['static']) + def fallback(self): + return 'fallback' + + @route('PROBE', [None]) + def wildcards(self): + return 'wildcards' + + router = Router(Routes()) + status = [] + + self.assertEqual( + ['fallback'], + [i for i in router({'REQUEST_METHOD': 'PROBE', 'PATH_INFO': '/static'}, lambda *args: None)]) + self.assertEqual( + ['wildcards'], + [i for i in router({'REQUEST_METHOD': 'PROBE', 'PATH_INFO': '/foo'}, lambda *args: None)]) + + def test_routes_FallbackForTailedPaths(self): + + class Routes(object): + + @fallbackroute('PROBE', ['static']) + def fallback(self, request): + return '/'.join(request.path) + + router = Router(Routes()) + status = [] + + self.assertEqual( + ['static'], + [i for i in router({'REQUEST_METHOD': 'PROBE', 'PATH_INFO': '/static'}, lambda *args: None)]) + self.assertEqual( + ['static/foo'], + [i for i in router({'REQUEST_METHOD': 'PROBE', 'PATH_INFO': '/static/foo'}, lambda *args: None)]) + self.assertEqual( + ['static/foo/bar'], + [i for i in router({'REQUEST_METHOD': 'PROBE', 'PATH_INFO': '/static/foo/bar'}, lambda *args: None)]) + + def test_routes_ParentClasses(self): + calls = [] + + class Parent(object): + + @route('PROBE') + def probe(self): + return 'probe' + + class Child(Parent): + pass + + router = Router(Child()) + + self.assertEqual( + ['probe'], + [i for i in router({'REQUEST_METHOD': 'PROBE', 'PATH_INFO': '/'}, lambda *args: None)]) + + def test_routes_OverrideInChildClass(self): + calls = [] + + class Parent(object): + + @route('PROBE') + def probe(self): + return 'probe-1' + + @route('COMMON') + def common(self): + return 'common' + + class Child(Parent): + + @route('PROBE') + def probe(self): + return 'probe-2' + + @route('PARTICULAR') + def particular(self): + return 'particular' + + router = Router(Child()) + + self.assertEqual( + ['probe-2'], + [i for i in router({'REQUEST_METHOD': 'PROBE', 'PATH_INFO': '/'}, lambda *args: None)]) + self.assertEqual( + ['common'], + [i for i in router({'REQUEST_METHOD': 'COMMON', 'PATH_INFO': '/'}, lambda *args: None)]) + self.assertEqual( + ['particular'], + [i for i in router({'REQUEST_METHOD': 'PARTICULAR', 'PATH_INFO': '/'}, lambda *args: None)]) + + def test_routes_Pre(self): + + class Routes(object): + + @route('PROBE') + def ok(self, request, response): + return request['probe'] + + @preroute + def preroute(self, op, request): + request['probe'] = 'request' + + router = Router(Routes()) + + self.assertEqual( + ['request'], + [i for i in router({'REQUEST_METHOD': 'PROBE', 'PATH_INFO': '/'}, lambda *args: None)]) + + def test_routes_Post(self): + postroutes = [] + + class Routes(object): + + @route('OK') + def ok(self): + return 'ok' + + @route('FAIL') + def fail(self, request, response): + raise Exception('fail') + + @postroute + def postroute(self, request, response, result, exception): + postroutes.append((result, str(exception))) + + router = Router(Routes()) + + self.assertEqual( + ['ok'], + [i for i in router({'REQUEST_METHOD': 'OK', 'PATH_INFO': '/'}, lambda *args: None)]) + self.assertEqual(('ok', 'None'), postroutes[-1]) + + self.assertEqual( + ['{"request": "/", "error": "fail"}'], + [i for i in router({'REQUEST_METHOD': 'FAIL', 'PATH_INFO': '/'}, lambda *args: None)]) + self.assertEqual((None, 'fail'), postroutes[-1]) + + def test_routes_WildcardsAsLastResort(self): + + class Routes(object): + + @route('PROBE', ['exists']) + def exists(self): + return 'exists' + + @route('PROBE', ['exists', 'deep']) + def exists_deep(self): + return 'exists/deep' + + @route('GET', [None]) + def wildcards(self): + return '*' + + @route('GET', [None, None]) + def wildcards_deep(self): + return '*/*' + + router = Router(Routes()) + status = [] + + self.assertEqual( + ['exists'], + [i for i in router({'REQUEST_METHOD': 'PROBE', 'PATH_INFO': '/exists'}, lambda *args: None)]) + self.assertEqual( + ['exists/deep'], + [i for i in router({'REQUEST_METHOD': 'PROBE', 'PATH_INFO': '/exists/deep'}, lambda *args: None)]) + self.assertEqual( + ['*'], + [i for i in router({'REQUEST_METHOD': 'GET', 'PATH_INFO': '/exists'}, lambda *args: None)]) + self.assertEqual( + ['*/*'], + [i for i in router({'REQUEST_METHOD': 'GET', 'PATH_INFO': '/exists/deep'}, lambda *args: None)]) + self.assertEqual( + ['*'], + [i for i in router({'REQUEST_METHOD': 'GET', 'PATH_INFO': '/foo'}, lambda *args: None)]) + self.assertEqual( + ['*/*'], + [i for i in router({'REQUEST_METHOD': 'GET', 'PATH_INFO': '/foo/bar'}, lambda *args: None)]) + + def test_Request_Read(self): + + class Stream(object): + + def __init__(self, value): + self.pos = 0 + self.value = value + + def read(self, size): + assert self.pos + size <= len(self.value) + result = self.value[self.pos:self.pos + size] + self.pos += size + return result + + request = Request({'PATH_INFO': '/', 'REQUEST_METHOD': 'GET', 'wsgi.input': Stream('123')}) + request.content_length = len(request.content_stream.value) + self.assertEqual('123', request.read()) + self.assertEqual('', request.read()) + self.assertEqual('', request.read(10)) + + request = Request({'PATH_INFO': '/', 'REQUEST_METHOD': 'GET', 'wsgi.input': Stream('123')}) + request.content_length = len(request.content_stream.value) + self.assertEqual('123', request.read(10)) + + request = Request({'PATH_INFO': '/', 'REQUEST_METHOD': 'GET', 'wsgi.input': Stream('123')}) + request.content_length = len(request.content_stream.value) + self.assertEqual('1', request.read(1)) + self.assertEqual('2', request.read(1)) + self.assertEqual('3', request.read()) + + def test_IntArguments(self): + + class Routes(object): + + @route('PROBE', arguments={'arg': int}) + def probe(self, arg): + return json.dumps(arg) + + router = Router(Routes()) + + self.assertEqual( + ['null'], + [i for i in router({ + 'REQUEST_METHOD': 'PROBE', + 'PATH_INFO': '/', + }, lambda *args: None)]) + self.assertEqual( + ['-1'], + [i for i in router({ + 'REQUEST_METHOD': 'PROBE', + 'PATH_INFO': '/', + 'QUERY_STRING': 'arg=-1', + }, lambda *args: None)]) + self.assertEqual( + ['2'], + [i for i in router({ + 'REQUEST_METHOD': 'PROBE', + 'PATH_INFO': '/', + 'QUERY_STRING': 'arg=1&arg=2', + }, lambda *args: None)]) + self.assertEqual( + ['0'], + [i for i in router({ + 'REQUEST_METHOD': 'PROBE', + 'PATH_INFO': '/', + 'QUERY_STRING': 'arg=', + }, lambda *args: None)]) + self.assertEqual( + ['null'], + [i for i in router({ + 'REQUEST_METHOD': 'PROBE', + 'PATH_INFO': '/', + }, lambda *args: None)]) + + def test_BoolArguments(self): + + class Routes(object): + + @route('PROBE', arguments={'arg': bool}) + def probe(self, arg): + return json.dumps(arg) + + router = Router(Routes()) + + self.assertEqual( + ['null'], + [i for i in router({ + 'REQUEST_METHOD': 'PROBE', + 'PATH_INFO': '/', + }, lambda *args: None)]) + self.assertEqual( + ['true'], + [i for i in router({ + 'REQUEST_METHOD': 'PROBE', + 'PATH_INFO': '/', + 'QUERY_STRING': 'arg=1', + }, lambda *args: None)]) + self.assertEqual( + ['true'], + [i for i in router({ + 'REQUEST_METHOD': 'PROBE', + 'PATH_INFO': '/', + 'QUERY_STRING': 'arg=on', + }, lambda *args: None)]) + self.assertEqual( + ['true'], + [i for i in router({ + 'REQUEST_METHOD': 'PROBE', + 'PATH_INFO': '/', + 'QUERY_STRING': 'arg=true', + }, lambda *args: None)]) + self.assertEqual( + ['false'], + [i for i in router({ + 'REQUEST_METHOD': 'PROBE', + 'PATH_INFO': '/', + 'QUERY_STRING': 'arg=foo', + }, lambda *args: None)]) + self.assertEqual( + ['true'], + [i for i in router({ + 'REQUEST_METHOD': 'PROBE', + 'PATH_INFO': '/', + 'QUERY_STRING': 'arg=', + }, lambda *args: None)]) + self.assertEqual( + ['true'], + [i for i in router({ + 'REQUEST_METHOD': 'PROBE', + 'PATH_INFO': '/', + 'QUERY_STRING': 'arg', + }, lambda *args: None)]) + self.assertEqual( + ['null'], + [i for i in router({ + 'REQUEST_METHOD': 'PROBE', + 'PATH_INFO': '/', + }, lambda *args: None)]) + + def test_ListArguments(self): + + class Routes(object): + + @route('PROBE', arguments={'arg': list}) + def probe(self, arg): + return json.dumps(arg) + + router = Router(Routes()) + + self.assertEqual( + ['null'], + [i for i in router({ + 'REQUEST_METHOD': 'PROBE', + 'PATH_INFO': '/', + }, lambda *args: None)]) + self.assertEqual( + ['["a1"]'], + [i for i in router({ + 'REQUEST_METHOD': 'PROBE', + 'PATH_INFO': '/', + 'QUERY_STRING': 'arg=a1', + }, lambda *args: None)]) + self.assertEqual( + ['["a1", "a2"]'], + [i for i in router({ + 'REQUEST_METHOD': 'PROBE', + 'PATH_INFO': '/', + 'QUERY_STRING': 'arg=a1,a2', + }, lambda *args: None)]) + self.assertEqual( + ['["a1", "a2", "a3"]'], + [i for i in router({ + 'REQUEST_METHOD': 'PROBE', + 'PATH_INFO': '/', + 'QUERY_STRING': 'arg=a1&arg=a2&arg=a3', + }, lambda *args: None)]) + self.assertEqual( + ['[]'], + [i for i in router({ + 'REQUEST_METHOD': 'PROBE', + 'PATH_INFO': '/', + 'QUERY_STRING': 'arg=', + }, lambda *args: None)]) + self.assertEqual( + ['null'], + [i for i in router({ + 'REQUEST_METHOD': 'PROBE', + 'PATH_INFO': '/', + }, lambda *args: None)]) + + def test_ArgumentDefaults(self): + + class Routes(object): + + @route('PROBE', arguments={'a1': -1, 'a2': False, 'a3': [None]}, mime_type='application/json') + def probe(self, a1, a2, a3): + return (a1, a2, a3) + + router = Router(Routes()) + + self.assertEqual( + ['[-1, false, [null]]'], + [i for i in router({ + 'REQUEST_METHOD': 'PROBE', + 'PATH_INFO': '/', + }, lambda *args: None)]) + self.assertEqual( + ['[1, true, ["3", "4"]]'], + [i for i in router({ + 'REQUEST_METHOD': 'PROBE', + 'PATH_INFO': '/', + 'QUERY_STRING': 'a1=1&a2=1&a3=3,4', + }, lambda *args: None)]) + self.assertEqual( + ['[0, true, []]'], + [i for i in router({ + 'REQUEST_METHOD': 'PROBE', + 'PATH_INFO': '/', + 'QUERY_STRING': 'a1=&a2=&a3=', + }, lambda *args: None)]) + self.assertEqual( + ['[-1, false, [null]]'], + [i for i in router({ + 'REQUEST_METHOD': 'PROBE', + 'PATH_INFO': '/', + }, lambda *args: None)]) + + def test_StreamedResponse(self): + + class CommandsProcessor(object): + + @route('GET') + def get_stream(self, response): + return StringIO('stream') + + router = Router(CommandsProcessor()) + + response = router({ + 'PATH_INFO': '/', + 'REQUEST_METHOD': 'GET', + }, + lambda *args: None) + self.assertEqual('stream', ''.join([i for i in response])) + + def test_EmptyResponse(self): + + class CommandsProcessor(object): + + @route('GET', [], '1', mime_type='application/octet-stream') + def get_binary(self, response): + pass + + @route('GET', [], '2', mime_type='application/json') + def get_json(self, response): + pass + + @route('GET', [], '3') + def no_get(self, response): + pass + + router = Router(CommandsProcessor()) + + response = router({ + 'PATH_INFO': '/', + 'REQUEST_METHOD': 'GET', + 'QUERY_STRING': 'cmd=1', + }, + lambda *args: None) + self.assertEqual('', ''.join([i for i in response])) + + response = router({ + 'PATH_INFO': '/', + 'REQUEST_METHOD': 'GET', + 'QUERY_STRING': 'cmd=2', + }, + lambda *args: None) + self.assertEqual('null', ''.join([i for i in response])) + + response = router({ + 'PATH_INFO': '/', + 'REQUEST_METHOD': 'GET', + 'QUERY_STRING': 'cmd=3', + }, + lambda *args: None) + self.assertEqual('', ''.join([i for i in response])) + + def test_StatusWOResult(self): + + class Status(http.Status): + status = '001 Status' + headers = {'status-header': 'value'} + + class CommandsProcessor(object): + + @route('GET') + def get(self, response): + raise Status('Status-Error') + + router = Router(CommandsProcessor()) + + response = [] + reply = router({ + 'PATH_INFO': '/', + 'REQUEST_METHOD': 'GET', + }, + lambda status, headers: response.extend([status, dict(headers)])) + error = json.dumps({'request': '/', 'error': 'Status-Error'}) + self.assertEqual(error, ''.join([i for i in reply])) + self.assertEqual([ + '001 Status', + {'content-length': str(len(error)), 'content-type': 'application/json', 'status-header': 'value'}, + ], + response) + + def test_ErrorInHEAD(self): + + class Status(http.Status): + status = '001 Status' + + class CommandsProcessor(object): + + @route('HEAD') + def get(self, response): + raise Status('Status-Error') + + router = Router(CommandsProcessor()) + + response = [] + reply = router({ + 'PATH_INFO': '/', + 'REQUEST_METHOD': 'HEAD', + }, + lambda status, headers: response.extend([status, dict(headers)])) + self.assertEqual('', ''.join([i for i in reply])) + self.assertEqual([ + '001 Status', + {'X-SN-error': '"Status-Error"'}, + ], + response) + + def test_StatusPass(self): + + class StatusPass(http.StatusPass): + status = '001 StatusPass' + headers = {'statuspass-header': 'value'} + result = 'result' + + class CommandsProcessor(object): + + @route('GET') + def get(self, response): + raise StatusPass('Status-Error') + + router = Router(CommandsProcessor()) + + response = [] + reply = router({ + 'PATH_INFO': '/', + 'REQUEST_METHOD': 'GET', + }, + lambda status, headers: response.extend([status, dict(headers)])) + error = '' + self.assertEqual(error, ''.join([i for i in reply])) + self.assertEqual([ + '001 StatusPass', + {'content-length': str(len(error)), 'statuspass-header': 'value'}, + ], + response) + + def test_BlobsRedirects(self): + URL = 'http://sugarlabs.org' + + class CommandsProcessor(object): + + @route('GET') + def get(self, response): + return Blob(url=URL) + + router = Router(CommandsProcessor()) + + response = [] + reply = router({ + 'PATH_INFO': '/', + 'REQUEST_METHOD': 'GET', + }, + lambda status, headers: response.extend([status, dict(headers)])) + error = '' + self.assertEqual(error, ''.join([i for i in reply])) + self.assertEqual([ + '303 See Other', + {'content-length': '0', 'location': URL}, + ], + response) + + def test_LastModified(self): + + class CommandsProcessor(object): + + @route('GET') + def get(self, request, response): + response.last_modified = 10 + return 'ok' + + router = Router(CommandsProcessor()) + + response = [] + reply = router({ + 'PATH_INFO': '/', + 'REQUEST_METHOD': 'GET', + }, + lambda status, headers: response.extend([status, dict(headers)])) + result = 'ok' + self.assertEqual(result, ''.join([i for i in reply])) + self.assertEqual([ + '200 OK', + {'last-modified': formatdate(10, localtime=False, usegmt=True), 'content-length': str(len(result))}, + ], + response) + + def test_IfModifiedSince(self): + + class CommandsProcessor(object): + + @route('GET') + def get(self, request): + if not request.if_modified_since or request.if_modified_since >= 10: + return 'ok' + else: + raise http.NotModified() + + router = Router(CommandsProcessor()) + + response = [] + reply = router({ + 'PATH_INFO': '/', + 'REQUEST_METHOD': 'GET', + }, + lambda status, headers: response.extend([status, dict(headers)])) + result = 'ok' + self.assertEqual(result, ''.join([i for i in reply])) + self.assertEqual([ + '200 OK', + {'content-length': str(len(result))}, + ], + response) + + response = [] + reply = router({ + 'PATH_INFO': '/', + 'REQUEST_METHOD': 'GET', + 'HTTP_IF_MODIFIED_SINCE': formatdate(11, localtime=False, usegmt=True), + }, + lambda status, headers: response.extend([status, dict(headers)])) + result = 'ok' + self.assertEqual(result, ''.join([i for i in reply])) + self.assertEqual([ + '200 OK', + {'content-length': str(len(result))}, + ], + response) + + response = [] + reply = router({ + 'PATH_INFO': '/', + 'REQUEST_METHOD': 'GET', + 'HTTP_IF_MODIFIED_SINCE': formatdate(9, localtime=False, usegmt=True), + }, + lambda status, headers: response.extend([status, dict(headers)])) + result = '' + self.assertEqual(result, ''.join([i for i in reply])) + self.assertEqual([ + '304 Not Modified', + {'content-length': str(len(result))}, + ], + response) + + def test_Request_MultipleQueryArguments(self): + request = Request({ + 'PATH_INFO': '/', + 'REQUEST_METHOD': 'GET', + 'QUERY_STRING': 'a1=v1&a2=v2&a1=v3&a3=v4&a1=v5&a3=v6', + }) + self.assertEqual( + {'a1': ['v1', 'v3', 'v5'], 'a2': 'v2', 'a3': ['v4', 'v6']}, + dict(request)) + + def test_Register_UrlPath(self): + self.assertEqual( + [], + Request({'REQUEST_METHOD': 'GET', 'PATH_INFO': ''}).path) + self.assertEqual( + [], + Request({'REQUEST_METHOD': 'GET', 'PATH_INFO': '/'}).path) + self.assertEqual( + ['foo'], + Request({'REQUEST_METHOD': 'GET', 'PATH_INFO': 'foo'}).path) + self.assertEqual( + ['foo', 'bar'], + Request({'REQUEST_METHOD': 'GET', 'PATH_INFO': 'foo/bar'}).path) + self.assertEqual( + ['foo', 'bar'], + Request({'REQUEST_METHOD': 'GET', 'PATH_INFO': '/foo/bar/'}).path) + self.assertEqual( + ['foo', 'bar'], + Request({'REQUEST_METHOD': 'GET', 'PATH_INFO': '///foo////bar////'}).path) + + def test_Request_FailOnRelativePaths(self): + self.assertRaises(RuntimeError, Request, {'REQUEST_METHOD': 'GET', 'PATH_INFO': '..'}) + self.assertRaises(RuntimeError, Request, {'REQUEST_METHOD': 'GET', 'PATH_INFO': '/..'}) + self.assertRaises(RuntimeError, Request, {'REQUEST_METHOD': 'GET', 'PATH_INFO': '/../'}) + self.assertRaises(RuntimeError, Request, {'REQUEST_METHOD': 'GET', 'PATH_INFO': '../bar'}) + self.assertRaises(RuntimeError, Request, {'REQUEST_METHOD': 'GET', 'PATH_INFO': '/foo/../bar'}) + self.assertRaises(RuntimeError, Request, {'REQUEST_METHOD': 'GET', 'PATH_INFO': '/foo/..'}) + + def test_Request_EmptyArguments(self): + request = Request({'QUERY_STRING': 'a&b&c', 'PATH_INFO': '/', 'REQUEST_METHOD': 'GET'}) + self.assertEqual('', request['a']) + self.assertEqual('', request['b']) + self.assertEqual('', request['c']) + + def test_Request_UpdateQueryOnSets(self): + request = Request({'QUERY_STRING': 'a&b=2&c', 'PATH_INFO': '/', 'REQUEST_METHOD': 'GET'}) + self.assertEqual('a&b=2&c', request.query) + + request['a'] = 'a' + self.assertEqual('a=a&c=&b=2', request.query) + + request['b'] = 'b' + self.assertEqual('a=a&c=&b=b', request.query) + + request['c'] = 'c' + self.assertEqual('a=a&c=c&b=b', request.query) + + def test_parse_accept_language(self): + self.assertEqual( + ['ru', 'en', 'es'], + _parse_accept_language(' ru , en , es')) + self.assertEqual( + ['ru', 'en', 'es'], + _parse_accept_language(' en;q=.4 , ru, es;q=0.1')) + self.assertEqual( + ['ru', 'en', 'es'], + _parse_accept_language('ru;q=1,en;q=1,es;q=0.5')) + self.assertEqual( + ['ru-ru', 'es-br'], + _parse_accept_language('ru-RU,es_BR')) + + def test_JsonpCallback(self): + + class CommandsProcessor(object): + + @route('GET') + def get(self, request): + return 'ok' + + router = Router(CommandsProcessor()) + + response = [] + reply = router({ + 'PATH_INFO': '/', + 'REQUEST_METHOD': 'GET', + 'QUERY_STRING': 'callback=foo', + }, + lambda status, headers: response.extend([status, dict(headers)])) + result = 'foo("ok");' + self.assertEqual(result, ''.join([i for i in reply])) + self.assertEqual([ + '200 OK', + {'content-length': str(len(result))}, + ], + 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('Foo-3', _filename(['foo', 3], 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): + self.touch(('blob.data', 'value')) + + class CommandsProcessor(object): + + @route('GET', [], '1') + def cmd1(self, request): + return Blob(name='foo', blob='blob.data') + + @route('GET', [], cmd='2') + def cmd2(self, request): + return Blob(filename='foo.bar', blob='blob.data') + + router = Router(CommandsProcessor()) + + response = [] + reply = router({ + 'PATH_INFO': '/', + 'REQUEST_METHOD': 'GET', + 'QUERY_STRING': 'cmd=1', + }, + lambda status, headers: response.extend([status, dict(headers)])) + result = 'value' + self.assertEqual(result, ''.join([i for i in reply])) + self.assertEqual([ + '200 OK', + { + '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"', + } + ], + response) + + response = [] + reply = router({ + 'PATH_INFO': '/', + 'REQUEST_METHOD': 'GET', + 'QUERY_STRING': 'cmd=2', + }, + lambda status, headers: response.extend([status, dict(headers)])) + result = 'value' + self.assertEqual(result, ''.join([i for i in reply])) + self.assertEqual([ + '200 OK', + { + '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.bar"', + } + ], + response) + + def test_DoNotOverrideContentLengthForHEAD(self): + + class CommandsProcessor(object): + + @route('HEAD', []) + def head(self, request, response): + response.content_length = 100 + + router = Router(CommandsProcessor()) + + response = [] + reply = router({ + 'PATH_INFO': '/', + 'REQUEST_METHOD': 'HEAD', + }, + lambda status, headers: response.extend([status, dict(headers)])) + self.assertEqual([], [i for i in reply]) + self.assertEqual([ + '200 OK', + {'content-length': '100'}, + ], + response) + + +if __name__ == '__main__': + tests.main() diff --git a/tests/units/toolkit/util.py b/tests/units/toolkit/toolkit.py index ada713d..b901583 100755 --- a/tests/units/toolkit/util.py +++ b/tests/units/toolkit/toolkit.py @@ -7,8 +7,8 @@ from cStringIO import StringIO from __init__ import tests -from sugar_network.toolkit import util -from sugar_network.toolkit.util import Seqno, Sequence +from sugar_network import toolkit +from sugar_network.toolkit import Seqno, Sequence class UtilTest(tests.Test): @@ -350,7 +350,7 @@ class UtilTest(tests.Test): result = [] stream = StringIO(string) while True: - line = util.readline(stream) + line = toolkit.readline(stream) if not line: break result.append(line) @@ -364,39 +364,39 @@ class UtilTest(tests.Test): self.assertEqual([' \n', ' b \n'], readlines(' \n b \n')) def test_Pool(self): - stack = util.Pool() + stack = toolkit.Pool() stack.add('a') stack.add('b') stack.add('c') - self.assertEqual(util.Pool.QUEUED, stack.get_state('a')) - self.assertEqual(util.Pool.QUEUED, stack.get_state('b')) - self.assertEqual(util.Pool.QUEUED, stack.get_state('c')) + self.assertEqual(toolkit.Pool.QUEUED, stack.get_state('a')) + self.assertEqual(toolkit.Pool.QUEUED, stack.get_state('b')) + self.assertEqual(toolkit.Pool.QUEUED, stack.get_state('c')) self.assertEqual( - [('c', util.Pool.ACTIVE), ('b', util.Pool.ACTIVE), ('a', util.Pool.ACTIVE)], + [('c', toolkit.Pool.ACTIVE), ('b', toolkit.Pool.ACTIVE), ('a', toolkit.Pool.ACTIVE)], [(i, stack.get_state(i)) for i in stack]) self.assertEqual( [], [i for i in stack]) - self.assertEqual(util.Pool.PASSED, stack.get_state('a')) - self.assertEqual(util.Pool.PASSED, stack.get_state('b')) - self.assertEqual(util.Pool.PASSED, stack.get_state('c')) + self.assertEqual(toolkit.Pool.PASSED, stack.get_state('a')) + self.assertEqual(toolkit.Pool.PASSED, stack.get_state('b')) + self.assertEqual(toolkit.Pool.PASSED, stack.get_state('c')) stack.rewind() - self.assertEqual(util.Pool.QUEUED, stack.get_state('a')) - self.assertEqual(util.Pool.QUEUED, stack.get_state('b')) - self.assertEqual(util.Pool.QUEUED, stack.get_state('c')) + self.assertEqual(toolkit.Pool.QUEUED, stack.get_state('a')) + self.assertEqual(toolkit.Pool.QUEUED, stack.get_state('b')) + self.assertEqual(toolkit.Pool.QUEUED, stack.get_state('c')) self.assertEqual( ['c', 'b', 'a'], [i for i in stack]) stack.add('c') - self.assertEqual(util.Pool.QUEUED, stack.get_state('c')) + self.assertEqual(toolkit.Pool.QUEUED, stack.get_state('c')) self.assertEqual( - [('c', util.Pool.ACTIVE)], + [('c', toolkit.Pool.ACTIVE)], [(i, stack.get_state(i)) for i in stack]) - self.assertEqual(util.Pool.PASSED, stack.get_state('c')) + self.assertEqual(toolkit.Pool.PASSED, stack.get_state('c')) stack.add('b') stack.add('a') @@ -410,17 +410,45 @@ class UtilTest(tests.Test): [i for i in stack]) stack.add('d') - self.assertEqual(util.Pool.QUEUED, stack.get_state('d')) + self.assertEqual(toolkit.Pool.QUEUED, stack.get_state('d')) self.assertEqual( - [('d', util.Pool.ACTIVE)], + [('d', toolkit.Pool.ACTIVE)], [(i, stack.get_state(i)) for i in stack]) - self.assertEqual(util.Pool.PASSED, stack.get_state('d')) + self.assertEqual(toolkit.Pool.PASSED, stack.get_state('d')) stack.rewind() self.assertEqual( ['d', 'a', 'b', 'c'], [i for i in stack]) + def test_gettext(self): + # Fallback to default lang + toolkit._default_lang = '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_lang = 'en-us' + self.assertEqual('right', toolkit.gettext({'a': 'wrong', 'en': 'right'}, 'probe')) + self.assertEqual('exact', toolkit.gettext({'a': 'wrong', 'en': 'right', 'probe': 'exact'}, 'probe')) + if __name__ == '__main__': tests.main() |