Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/tests/units/client/offline_routes.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/units/client/offline_routes.py')
-rwxr-xr-xtests/units/client/offline_routes.py557
1 files changed, 419 insertions, 138 deletions
diff --git a/tests/units/client/offline_routes.py b/tests/units/client/offline_routes.py
index 961bbb5..2a8692b 100755
--- a/tests/units/client/offline_routes.py
+++ b/tests/units/client/offline_routes.py
@@ -1,108 +1,34 @@
#!/usr/bin/env python
# sugar-lint: disable
+import json
+from cStringIO import StringIO
from os.path import exists
from __init__ import tests, src_root
from sugar_network import client, model
-from sugar_network.client import IPCConnection, clones
+from sugar_network.client import IPCConnection, implementations, packagekit
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
+from sugar_network.toolkit import coroutine, http, lsb_release
class OfflineRoutes(tests.Test):
- def setUp(self):
- tests.Test.setUp(self)
- 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()
-
- def test_NoAuthors(self):
- ipc = IPCConnection()
-
- guid = ipc.post(['context'], {
- 'type': 'activity',
- 'title': 'title',
- 'summary': 'summary',
- 'description': 'description',
- })
- self.assertEqual(
- {},
- self.home_volume['context'].get(guid)['author'])
- self.assertEqual(
- [],
- ipc.get(['context', guid, 'author']))
-
- def test_HandleDeletes(self):
- ipc = IPCConnection()
-
- guid = ipc.post(['context'], {
- 'type': 'activity',
- 'title': 'title',
- 'summary': 'summary',
- 'description': 'description',
- })
-
- guid_path = 'db/context/%s/%s' % (guid[:2], guid)
- assert exists(guid_path)
-
- ipc.delete(['context', guid])
- self.assertRaises(http.NotFound, ipc.get, ['context', guid])
- assert not exists(guid_path)
+ def setUp(self, fork_num=0):
+ tests.Test.setUp(self, fork_num)
+ self.override(implementations, '_activity_id_new', lambda: 'activity_id')
def test_whoami(self):
- ipc = IPCConnection()
+ ipc = self.start_offline_client()
self.assertEqual(
{'guid': tests.UID, 'roles': []},
ipc.get(cmd='whoami'))
- def test_clone(self):
- ipc = IPCConnection()
-
- context = ipc.post(['context'], {
- 'type': 'activity',
- 'title': 'title',
- 'summary': 'summary',
- 'description': 'description',
- })
-
- self.assertRaises(RuntimeError, ipc.put, ['context', context], 1, cmd='clone')
-
- def test_favorite(self):
- ipc = IPCConnection()
-
- context = ipc.post(['context'], {
- 'type': 'activity',
- 'title': 'title',
- 'summary': 'summary',
- 'description': 'description',
- })
-
- self.assertEqual(
- {'favorite': False},
- ipc.get(['context', context], reply=['favorite']))
-
- ipc.put(['context', context], True, cmd='favorite')
-
- self.assertEqual(
- {'favorite': True},
- ipc.get(['context', context], reply=['favorite']))
-
- ipc.put(['context', context], False, cmd='favorite')
-
- self.assertEqual(
- {'favorite': False},
- ipc.get(['context', context], reply=['favorite']))
-
- def test_subscribe(self):
- ipc = IPCConnection()
+ def test_Events(self):
+ ipc = self.start_offline_client()
events = []
def read_events():
@@ -117,13 +43,11 @@ class OfflineRoutes(tests.Test):
'summary': 'summary',
'description': 'description',
})
- coroutine.dispatch()
ipc.put(['context', guid], {
'title': 'title_2',
})
- coroutine.dispatch()
ipc.delete(['context', guid])
- coroutine.sleep(.5)
+ coroutine.sleep(.1)
job.kill()
self.assertEqual([
@@ -133,8 +57,71 @@ class OfflineRoutes(tests.Test):
],
events)
+ def test_Feeds(self):
+ ipc = self.start_offline_client()
+
+ context = ipc.post(['context'], {
+ 'type': 'activity',
+ 'title': 'title',
+ 'summary': 'summary',
+ 'description': 'description',
+ })
+ impl1 = ipc.post(['implementation'], {
+ 'context': context,
+ 'license': 'GPLv3+',
+ 'version': '1',
+ 'stability': 'stable',
+ 'notes': '',
+ })
+ self.home_volume['implementation'].update(impl1, {'data': {
+ 'spec': {'*-*': {}},
+ }})
+ impl2 = ipc.post(['implementation'], {
+ 'context': context,
+ 'license': 'GPLv3+',
+ 'version': '2',
+ 'stability': 'stable',
+ 'notes': '',
+ })
+ self.home_volume['implementation'].update(impl2, {'data': {
+ 'spec': {'*-*': {
+ 'requires': {
+ 'dep1': {},
+ 'dep2': {'restrictions': [['1', '2']]},
+ 'dep3': {'restrictions': [[None, '2']]},
+ 'dep4': {'restrictions': [['3', None]]},
+ },
+ }},
+ }})
+
+ self.assertEqual({
+ 'implementations': [
+ {
+ 'version': '1',
+ 'arch': '*-*',
+ 'stability': 'stable',
+ 'guid': impl1,
+ 'license': ['GPLv3+'],
+ },
+ {
+ 'version': '2',
+ 'arch': '*-*',
+ 'stability': 'stable',
+ 'guid': impl2,
+ 'requires': {
+ 'dep1': {},
+ 'dep2': {'restrictions': [['1', '2']]},
+ 'dep3': {'restrictions': [[None, '2']]},
+ 'dep4': {'restrictions': [['3', None]]},
+ },
+ 'license': ['GPLv3+'],
+ },
+ ],
+ },
+ ipc.get(['context', context], cmd='feed'))
+
def test_BLOBs(self):
- ipc = IPCConnection()
+ ipc = self.start_offline_client()
guid = ipc.post(['context'], {
'type': 'activity',
@@ -164,71 +151,365 @@ class OfflineRoutes(tests.Test):
[{'icon': 'http://127.0.0.1:5555/static/images/missing.png'}],
ipc.get(['context'], reply=['icon'])['result'])
- def test_Feeds(self):
- self.touch(('Activities/activity-1/activity/activity.info', [
- '[Activity]',
- 'name = TestActivity',
- 'bundle_id = bundle_id',
- 'exec = false',
- 'icon = icon',
- 'activity_version = 1',
- 'license = Public Domain',
- ]))
- self.touch(('Activities/activity-2/activity/activity.info', [
+ def test_favorite(self):
+ ipc = self.start_offline_client()
+ events = []
+
+ def read_events():
+ for event in ipc.subscribe(event='!commit'):
+ events.append(event)
+ coroutine.spawn(read_events)
+ coroutine.dispatch()
+
+ context1 = ipc.post(['context'], {
+ 'type': 'activity',
+ 'title': 'title1',
+ 'summary': 'summary',
+ 'description': 'description',
+ })
+ context2 = ipc.post(['context'], {
+ 'type': 'activity',
+ 'title': 'title2',
+ 'summary': 'summary',
+ 'description': 'description',
+ })
+
+ self.assertEqual(
+ sorted([]),
+ sorted(ipc.get(['context'], layer='favorite')['result']))
+ self.assertEqual(
+ sorted([{'guid': context1}, {'guid': context2}]),
+ sorted(ipc.get(['context'], layer='local')['result']))
+ self.assertEqual(
+ sorted([{'guid': context1}, {'guid': context2}]),
+ sorted(ipc.get(['context'])['result']))
+ self.assertEqual(
+ sorted([{'guid': context1, 'layer': ['local']}, {'guid': context2, 'layer': ['local']}]),
+ sorted(ipc.get(['context'], reply='layer')['result']))
+ self.assertEqual({'layer': ['local']}, ipc.get(['context', context1], reply='layer'))
+ self.assertEqual(['local'], ipc.get(['context', context1, 'layer']))
+ self.assertEqual({'layer': ['local']}, ipc.get(['context', context2], reply='layer'))
+ self.assertEqual(['local'], ipc.get(['context', context2, 'layer']))
+
+ del events[:]
+ ipc.put(['context', context1], True, cmd='favorite')
+ coroutine.sleep(.1)
+
+ self.assertEqual(
+ {'guid': context1, 'resource': 'context', 'event': 'update'},
+ events[-1])
+ self.assertEqual(
+ sorted([{'guid': context1}]),
+ sorted(ipc.get(['context'], layer='favorite')['result']))
+ self.assertEqual(
+ sorted([{'guid': context1}, {'guid': context2}]),
+ sorted(ipc.get(['context'], layer='local')['result']))
+ self.assertEqual(
+ sorted([{'guid': context1}, {'guid': context2}]),
+ sorted(ipc.get(['context'])['result']))
+ self.assertEqual(
+ sorted([{'guid': context1, 'layer': ['favorite', 'local']}, {'guid': context2, 'layer': ['local']}]),
+ sorted(ipc.get(['context'], reply='layer')['result']))
+ self.assertEqual({'layer': ['favorite', 'local']}, ipc.get(['context', context1], reply='layer'))
+ self.assertEqual(['favorite', 'local'], ipc.get(['context', context1, 'layer']))
+ self.assertEqual({'layer': ['local']}, ipc.get(['context', context2], reply='layer'))
+ self.assertEqual(['local'], ipc.get(['context', context2, 'layer']))
+
+ del events[:]
+ ipc.put(['context', context2], True, cmd='favorite')
+ coroutine.sleep(.1)
+
+ self.assertEqual(
+ {'guid': context2, 'resource': 'context', 'event': 'update'},
+ events[-1])
+ self.assertEqual(
+ sorted([{'guid': context1}, {'guid': context2}]),
+ sorted(ipc.get(['context'], layer='favorite')['result']))
+ self.assertEqual(
+ sorted([{'guid': context1}, {'guid': context2}]),
+ sorted(ipc.get(['context'], layer='local')['result']))
+ self.assertEqual(
+ sorted([{'guid': context1}, {'guid': context2}]),
+ sorted(ipc.get(['context'])['result']))
+ self.assertEqual(
+ sorted([{'guid': context1, 'layer': ['favorite', 'local']}, {'guid': context2, 'layer': ['favorite', 'local']}]),
+ sorted(ipc.get(['context'], reply='layer')['result']))
+ self.assertEqual({'layer': ['favorite', 'local']}, ipc.get(['context', context1], reply='layer'))
+ self.assertEqual(['favorite', 'local'], ipc.get(['context', context1, 'layer']))
+ self.assertEqual({'layer': ['favorite', 'local']}, ipc.get(['context', context2], reply='layer'))
+ self.assertEqual(['favorite', 'local'], ipc.get(['context', context2, 'layer']))
+
+ del events[:]
+ ipc.put(['context', context1], False, cmd='favorite')
+ coroutine.sleep(.1)
+
+ self.assertEqual(
+ {'guid': context1, 'resource': 'context', 'event': 'update'},
+ events[-1])
+ self.assertEqual(
+ sorted([{'guid': context2}]),
+ sorted(ipc.get(['context'], layer='favorite')['result']))
+ self.assertEqual(
+ sorted([{'guid': context1}, {'guid': context2}]),
+ sorted(ipc.get(['context'], layer='local')['result']))
+ self.assertEqual(
+ sorted([{'guid': context1}, {'guid': context2}]),
+ sorted(ipc.get(['context'])['result']))
+ self.assertEqual(
+ sorted([{'guid': context1, 'layer': ['local']}, {'guid': context2, 'layer': ['favorite', 'local']}]),
+ sorted(ipc.get(['context'], reply='layer')['result']))
+ self.assertEqual({'layer': ['local']}, ipc.get(['context', context1], reply='layer'))
+ self.assertEqual(['local'], ipc.get(['context', context1, 'layer']))
+ self.assertEqual({'layer': ['favorite', 'local']}, ipc.get(['context', context2], reply='layer'))
+ self.assertEqual(['favorite', 'local'], ipc.get(['context', context2, 'layer']))
+
+ def test_launch_Activity(self):
+ local = self.start_online_client()
+ ipc = IPCConnection()
+
+ blob = self.zips(['TestActivity/activity/activity.info', [
'[Activity]',
'name = TestActivity',
'bundle_id = bundle_id',
'exec = true',
'icon = icon',
- 'activity_version = 2',
- 'license = Public Domain',
- 'requires = dep1; dep2 = 1; dep3 < 2; dep4 >= 3',
- ]))
+ 'activity_version = 1',
+ 'license=Public Domain',
+ ]])
+ impl = ipc.upload(['implementation'], StringIO(blob), cmd='release', initial=True)
+
+ ipc.put(['context', 'bundle_id'], True, cmd='clone')
+ solution = [{
+ 'guid': impl,
+ 'context': 'bundle_id',
+ 'extract': 'TestActivity',
+ 'license': ['Public Domain'],
+ 'stability': 'stable',
+ 'version': '1',
+ 'command': ['true'],
+ 'path': tests.tmpdir + '/client/implementation/%s/%s/data.blob' % (impl[:2], impl),
+ }]
+ assert local['implementation'].exists(impl)
+ self.assertEqual(
+ [client.api_url.value, ['stable'], solution],
+ json.load(file('cache/solutions/bu/bundle_id')))
- ipc = IPCConnection()
- monitor = coroutine.spawn(clones.monitor, self.home_volume['context'], ['Activities'])
+ self.node.stop()
+ coroutine.sleep(.1)
+
+ def read_events():
+ for event in ipc.subscribe(event='!commit'):
+ events.append(event)
+ events = []
+ coroutine.spawn(read_events)
+ coroutine.dispatch()
+
+ ipc.get(['context', 'bundle_id'], cmd='launch', foo='bar')
+ coroutine.sleep(.1)
+
+ log_path = tests.tmpdir + '/.sugar/default/logs/bundle_id.log'
+ self.assertEqual([
+ {'event': 'exec', 'cmd': 'launch', 'guid': 'bundle_id', 'foo': 'bar', 'args': ['true', '-b', 'bundle_id', '-a', 'activity_id'], 'activity_id': 'activity_id', 'log_path': log_path, 'solution': solution},
+ {'event': 'exit', 'cmd': 'launch', 'guid': 'bundle_id', 'foo': 'bar', 'args': ['true', '-b', 'bundle_id', '-a', 'activity_id'], 'activity_id': 'activity_id', 'log_path': log_path, 'solution': solution},
+ ],
+ events)
+ assert local['implementation'].exists(impl)
+ self.assertEqual(
+ [client.api_url.value, ['stable'], solution],
+ json.load(file('cache/solutions/bu/bundle_id')))
+
+ def test_ServiceUnavailableWhileSolving(self):
+ ipc = self.start_offline_client()
+
+ def read_events():
+ for event in ipc.subscribe(event='!commit'):
+ events.append(event)
+ events = []
+ coroutine.spawn(read_events)
coroutine.dispatch()
+ self.assertRaises(http.ServiceUnavailable, ipc.get, ['context', 'foo'], cmd='launch')
+ coroutine.dispatch()
self.assertEqual({
- 'name': 'TestActivity',
- 'implementations': [
- {
- 'version': '1',
- 'arch': '*-*',
- 'commands': {
- 'activity': {
- 'exec': 'false',
- },
- },
- 'stability': 'stable',
- 'guid': tests.tmpdir + '/Activities/activity-1',
- 'requires': {},
+ 'event': 'failure',
+ 'method': 'GET',
+ 'guid': 'foo',
+ 'cmd': 'launch',
+ 'resource': 'context',
+ 'prop': None,
+ 'exception': 'ServiceUnavailable',
+ 'error': "Resource 'foo' does not exist in 'context'",
+ },
+ events[-1])
+
+ context = ipc.post(['context'], {
+ 'type': 'activity',
+ 'title': 'title',
+ 'summary': 'summary',
+ 'description': 'description',
+ })
+ self.assertRaises(http.ServiceUnavailable, ipc.get, ['context', context], cmd='launch')
+ coroutine.dispatch()
+ self.assertEqual({
+ 'event': 'failure',
+ 'method': 'GET',
+ 'guid': context,
+ 'cmd': 'launch',
+ 'resource': 'context',
+ 'prop': None,
+ 'exception': 'ServiceUnavailable',
+ 'error': """\
+Can't find all required implementations:
+- %s -> (problem)
+ No known implementations at all""" % context,
+ },
+ events[-1])
+
+ impl = ipc.post(['implementation'], {
+ 'context': context,
+ 'license': 'GPLv3+',
+ 'version': '1',
+ 'stability': 'stable',
+ })
+ self.home_volume['implementation'].update(impl, {'data': {
+ 'spec': {
+ '*-*': {
+ 'commands': {'activity': {'exec': 'true'}},
+ 'requires': {'dep': {}},
},
- {
- 'version': '2',
- 'arch': '*-*',
- 'commands': {
- 'activity': {
- 'exec': 'true',
- },
- },
+ },
+ }})
+
+ self.assertRaises(http.ServiceUnavailable, ipc.get, ['context', context], cmd='launch')
+ coroutine.dispatch()
+ self.assertEqual({
+ 'event': 'failure',
+ 'method': 'GET',
+ 'guid': context,
+ 'cmd': 'launch',
+ 'resource': 'context',
+ 'prop': None,
+ 'exception': 'ServiceUnavailable',
+ 'error': """\
+Can't find all required implementations:
+- %s -> 1 (%s)
+- dep -> (problem)
+ No known implementations at all""" % (context, impl),
+ },
+ events[-1])
+
+ assert not exists('cache/solutions/%s/%s' % (context[:2], context))
+
+ def test_ServiceUnavailableWhileInstalling(self):
+ ipc = self.start_offline_client()
+
+ context = ipc.post(['context'], {
+ 'type': 'activity',
+ 'title': 'title',
+ 'summary': 'summary',
+ 'description': 'description',
+ })
+ impl = ipc.post(['implementation'], {
+ 'context': context,
+ 'license': 'GPLv3+',
+ 'version': '1',
+ 'stability': 'stable',
+ })
+ self.home_volume['implementation'].update(impl, {'data': {
+ 'spec': {
+ '*-*': {
+ 'commands': {'activity': {'exec': 'true'}},
+ 'requires': {'dep': {}},
+ },
+ },
+ }})
+ ipc.post(['context'], {
+ 'guid': 'dep',
+ 'type': 'package',
+ 'title': 'title',
+ 'summary': 'summary',
+ 'description': 'description',
+ 'aliases': {
+ lsb_release.distributor_id(): {
+ 'status': 'success',
+ 'binary': [['dep.bin']],
+ },
+ },
+ })
+
+ def resolve(names):
+ return dict([(i, {'name': i, 'pk_id': i, 'version': '0', 'arch': '*', 'installed': False}) for i in names])
+ self.override(packagekit, 'resolve', resolve)
+
+ def read_events():
+ for event in ipc.subscribe(event='!commit'):
+ events.append(event)
+ events = []
+ coroutine.spawn(read_events)
+ coroutine.dispatch()
+
+ self.assertRaises(http.ServiceUnavailable, ipc.get, ['context', context], cmd='launch')
+ coroutine.dispatch()
+ self.assertEqual({
+ 'event': 'failure',
+ 'method': 'GET',
+ 'guid': context,
+ 'cmd': 'launch',
+ 'resource': 'context',
+ 'prop': None,
+ 'exception': 'ServiceUnavailable',
+ 'error': 'Installation is not available in offline',
+ 'solution': [
+ { 'guid': impl,
+ 'context': context,
+ 'license': ['GPLv3+'],
'stability': 'stable',
- 'guid': tests.tmpdir + '/Activities/activity-2',
- 'requires': {
- 'dep1': {},
- 'dep2': {'restrictions': [['1', '2']]},
- 'dep3': {'restrictions': [[None, '2']]},
- 'dep4': {'restrictions': [['3', None]]},
- },
+ 'version': '1',
+ 'command': ['true'],
+ },
+ { 'guid': 'dep',
+ 'context': 'dep',
+ 'install': [{'arch': '*', 'installed': False, 'name': 'dep.bin', 'pk_id': 'dep.bin', 'version': '0'}],
+ 'license': None,
+ 'stability': 'packaged',
+ 'version': '0',
},
],
},
- ipc.get(['context', 'bundle_id'], cmd='feed'))
+ events[-1])
- def test_LocalAPIShouldDuplicateNodeButWith503Response(self):
- ipc = IPCConnection()
- self.assertRaises(http.ServiceUnavailable, ipc.get, ['context', 'foo'], cmd='feed')
- self.assertRaises(http.ServiceUnavailable, ipc.get, ['packages', 'foo', 'bar'])
+ def test_NoAuthors(self):
+ ipc = self.start_offline_client()
+
+ guid = ipc.post(['context'], {
+ 'type': 'activity',
+ 'title': 'title',
+ 'summary': 'summary',
+ 'description': 'description',
+ })
+ self.assertEqual(
+ {},
+ self.home_volume['context'].get(guid)['author'])
+ self.assertEqual(
+ [],
+ ipc.get(['context', guid, 'author']))
+
+ def test_HandleDeletes(self):
+ ipc = self.start_offline_client()
+
+ guid = ipc.post(['context'], {
+ 'type': 'activity',
+ 'title': 'title',
+ 'summary': 'summary',
+ 'description': 'description',
+ })
+
+ guid_path = 'db/context/%s/%s' % (guid[:2], guid)
+ assert exists(guid_path)
+
+ ipc.delete(['context', guid])
+ self.assertRaises(http.NotFound, ipc.get, ['context', guid])
+ assert not exists(guid_path)
if __name__ == '__main__':