Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAleksey Lim <alsroot@sugarlabs.org>2013-11-02 12:16:48 (GMT)
committer Aleksey Lim <alsroot@sugarlabs.org>2013-11-02 12:16:48 (GMT)
commit1eff810db0c62174e8d445f15e4b1604665ac18d (patch)
tree53955ffeb020e12e0c47d347ef8d3f2028a08de7
parentbff48d640dbd44d614245f016d8fed29d9665fbc (diff)
Not only original authors can upload new implementations
-rw-r--r--sugar_network/client/implementations.py10
-rw-r--r--sugar_network/client/solver.py2
-rw-r--r--sugar_network/db/directory.py19
-rw-r--r--sugar_network/model/implementation.py9
-rw-r--r--sugar_network/model/routes.py6
-rw-r--r--sugar_network/node/routes.py48
-rw-r--r--tests/data/node/implementation/im/implementation/layer2
-rw-r--r--tests/data/node/implementation/im/implementation2/layer2
-rwxr-xr-xtests/units/client/implementations.py9
-rwxr-xr-xtests/units/client/offline_routes.py22
-rwxr-xr-xtests/units/client/online_routes.py71
-rwxr-xr-xtests/units/client/solver.py41
-rwxr-xr-xtests/units/db/resource.py41
-rwxr-xr-xtests/units/model/implementation.py51
-rwxr-xr-xtests/units/node/node.py36
15 files changed, 295 insertions, 74 deletions
diff --git a/sugar_network/client/implementations.py b/sugar_network/client/implementations.py
index 3cd59de..f78df33 100644
--- a/sugar_network/client/implementations.py
+++ b/sugar_network/client/implementations.py
@@ -261,11 +261,7 @@ class Routes(object):
with file(data_path, 'wb') as f:
shutil.copyfileobj(blob, f)
impl = sel.copy()
- impl['layer'] = []
- impl['ctime'] = impl['mtime'] = int(time.time())
- impl['author'] = {}
- impl['notes'] = ''
- impl['tags'] = []
+ impl['mtime'] = impl['ctime']
impls.create(impl)
return cache_call(guid, size)
except Exception:
@@ -320,7 +316,9 @@ class Routes(object):
guid = basename(os.readlink(context.path('.clone')))
impl = self._volume['implementation'].get(guid)
response.meta = impl.properties([
- 'guid', 'context', 'license', 'version', 'stability', 'data'])
+ 'guid', 'ctime', 'layer', 'author', 'tags',
+ 'context', 'version', 'stability', 'license', 'notes', 'data',
+ ])
return impl.meta('data')
diff --git a/sugar_network/client/solver.py b/sugar_network/client/solver.py
index 9f29e1d..0b24579 100644
--- a/sugar_network/client/solver.py
+++ b/sugar_network/client/solver.py
@@ -194,7 +194,7 @@ def _load_feed(context):
feed_content = None
try:
feed_content = _call(method='GET', path=['context', context],
- cmd='feed', stability=_stability,
+ cmd='feed', layer='origin', stability=_stability,
distro=lsb_release.distributor_id())
_logger.trace('[%s] Found feed: %r', context, feed_content)
except http.ServiceUnavailable:
diff --git a/sugar_network/db/directory.py b/sugar_network/db/directory.py
index 86afed2..6bb2d70 100644
--- a/sugar_network/db/directory.py
+++ b/sugar_network/db/directory.py
@@ -225,6 +225,25 @@ class Directory(object):
self.commit()
self.checkpoint()
+ def patch(self, guid, props, accept_language=None):
+ if not accept_language:
+ accept_language = toolkit.default_lang()
+ orig = self.get(guid)
+ patch = {}
+ for prop, value in (props or {}).items():
+ if orig[prop] == value:
+ continue
+ if isinstance(self.metadata[prop], StoredProperty) and \
+ self.metadata[prop].localized:
+ if isinstance(value, dict):
+ orig_value = dict([(i, orig[prop].get(i)) for i in value])
+ if orig_value == value:
+ continue
+ elif orig.get(prop, accept_language) == value:
+ continue
+ patch[prop] = value
+ return patch
+
def diff(self, seq, exclude_seq=None, **params):
if exclude_seq is None:
exclude_seq = []
diff --git a/sugar_network/model/implementation.py b/sugar_network/model/implementation.py
index f1c1c23..afeda82 100644
--- a/sugar_network/model/implementation.py
+++ b/sugar_network/model/implementation.py
@@ -19,7 +19,6 @@ from sugar_network import db, model
from sugar_network.toolkit.router import ACL
from sugar_network.toolkit.licenses import GOOD_LICENSES
from sugar_network.toolkit.spec import parse_version
-from sugar_network.toolkit import http, enforce
class Implementation(db.Resource):
@@ -31,10 +30,10 @@ class Implementation(db.Resource):
@context.setter
def context(self, value):
- authors = self.volume['context'].get(value)['author']
- enforce(not self.request.principal and not authors or
- self.request.principal in authors, http.Forbidden,
- 'Only Context authors can submit new Implementations')
+ if self.request.principal:
+ authors = self.volume['context'].get(value)['author']
+ if self.request.principal in authors:
+ self['layer'] = ('origin',) + tuple(self.layer)
return value
@db.indexed_property(prefix='L', full_text=True, typecast=[GOOD_LICENSES],
diff --git a/sugar_network/model/routes.py b/sugar_network/model/routes.py
index 17da118..6abb758 100644
--- a/sugar_network/model/routes.py
+++ b/sugar_network/model/routes.py
@@ -37,8 +37,10 @@ class VolumeRoutes(db.Routes):
impls, __ = implementations.find(context=context.guid,
not_layer='deleted', **request)
for impl in impls:
- version = impl.properties(
- ['guid', 'version', 'stability', 'license'])
+ version = impl.properties([
+ 'guid', 'ctime', 'layer', 'author', 'tags',
+ 'version', 'stability', 'license', 'notes',
+ ])
if context['dependencies']:
requires = version.setdefault('requires', {})
for i in context['dependencies']:
diff --git a/sugar_network/node/routes.py b/sugar_network/node/routes.py
index 32ba497..80caba4 100644
--- a/sugar_network/node/routes.py
+++ b/sugar_network/node/routes.py
@@ -159,7 +159,7 @@ class NodeRoutes(model.VolumeRoutes, model.FrontRoutes):
@route('POST', ['implementation'], cmd='submit',
arguments={'initial': False},
- mime_type='application/json', acl=ACL.AUTH | ACL.AUTHOR)
+ mime_type='application/json', acl=ACL.AUTH)
def submit_implementation(self, request, document):
with toolkit.NamedTemporaryFile() as blob:
shutil.copyfileobj(request.content_stream, blob)
@@ -400,8 +400,10 @@ class NodeRoutes(model.VolumeRoutes, model.FrontRoutes):
result = request.call(method=request.method,
path=['implementation', impl['guid'], 'data'],
response=response)
- response.meta = impl.properties(
- ['guid', 'context', 'license', 'version', 'stability'])
+ response.meta = impl.properties([
+ 'guid', 'ctime', 'layer', 'author', 'tags',
+ 'context', 'version', 'stability', 'license', 'notes',
+ ])
response.meta['data'] = data = impl.meta('data')
for key in ('mtime', 'seqno', 'blob'):
if key in data:
@@ -418,7 +420,9 @@ def load_bundle(volume, request, bundle_path):
data = impl.setdefault('data', {})
data['blob'] = bundle_path
contexts = volume['context']
- context = None
+ context = impl.get('context')
+ context_meta = None
+ impls = volume['implementation']
try:
bundle = Bundle(bundle_path, mime_type='application/zip')
@@ -434,11 +438,11 @@ def load_bundle(volume, request, bundle_path):
for arcname in bundle.get_names():
unpack_size += bundle.getmember(arcname).size
spec = bundle.get_spec()
- context = _load_context_metadata(bundle, spec)
+ context_meta = _load_context_metadata(bundle, spec)
if 'requires' in impl:
spec.requires.update(parse_requires(impl.pop('requires')))
- impl['context'] = spec['context']
+ context = impl['context'] = spec['context']
impl['version'] = spec['version']
impl['stability'] = spec['stability']
impl['license'] = spec['license']
@@ -449,37 +453,35 @@ def load_bundle(volume, request, bundle_path):
data['unpack_size'] = unpack_size
data['mime_type'] = 'application/vnd.olpc-sugar'
- if initial and not contexts.exists(impl['context']):
- context['guid'] = impl['context']
- context['type'] = 'activity'
- request.call(method='POST', path=['context'], content=context)
- context = None
+ if initial and not contexts.exists(context):
+ context_meta['guid'] = context
+ context_meta['type'] = 'activity'
+ request.call(method='POST', path=['context'], content=context_meta)
+ context_meta = None
- enforce('context' in impl, 'Context is not specified')
+ enforce(context, 'Context is not specified')
enforce('version' in impl, 'Version is not specified')
- enforce(context_type in contexts.get(impl['context'])['type'],
+ enforce(context_type in contexts.get(context)['type'],
http.BadRequest, 'Inappropriate bundle type')
if impl.get('license') in (None, EMPTY_LICENSE):
- existing, total = volume['implementation'].find(
- context=impl['context'], order_by='-version',
- not_layer='deleted')
+ existing, total = impls.find(
+ context=context, order_by='-version', not_layer='deleted')
enforce(total, 'License is not specified')
impl['license'] = next(existing)['license']
yield impl
- existing, __ = volume['implementation'].find(
- context=impl['context'], version=impl['version'],
- not_layer='deleted')
+ existing, __ = impls.find(
+ context=context, version=impl['version'], not_layer='deleted')
impl['guid'] = \
request.call(method='POST', path=['implementation'], content=impl)
for i in existing:
layer = i['layer'] + ['deleted']
- volume['implementation'].update(i.guid, {'layer': layer})
+ impls.update(i.guid, {'layer': layer})
- if context:
- request.call(method='PUT', path=['context', impl['context']],
- content=context)
+ patch = contexts.patch(context, context_meta)
+ if patch and 'origin' in impls.get(impl['guid']).layer:
+ request.call(method='PUT', path=['context', context], content=patch)
def _load_context_metadata(bundle, spec):
diff --git a/tests/data/node/implementation/im/implementation/layer b/tests/data/node/implementation/im/implementation/layer
index ed16e06..1fe70e4 100644
--- a/tests/data/node/implementation/im/implementation/layer
+++ b/tests/data/node/implementation/im/implementation/layer
@@ -1 +1 @@
-{"seqno": 3, "value": ["public"]} \ No newline at end of file
+{"seqno": 3, "value": ["origin"]} \ No newline at end of file
diff --git a/tests/data/node/implementation/im/implementation2/layer b/tests/data/node/implementation/im/implementation2/layer
index ed16e06..1fe70e4 100644
--- a/tests/data/node/implementation/im/implementation2/layer
+++ b/tests/data/node/implementation/im/implementation2/layer
@@ -1 +1 @@
-{"seqno": 3, "value": ["public"]} \ No newline at end of file
+{"seqno": 3, "value": ["origin"]} \ No newline at end of file
diff --git a/tests/units/client/implementations.py b/tests/units/client/implementations.py
index e63d132..d8173e9 100755
--- a/tests/units/client/implementations.py
+++ b/tests/units/client/implementations.py
@@ -154,6 +154,11 @@ class Implementations(tests.Test):
'context': 'bundle_id',
'path': tests.tmpdir + '/client/implementation/%s/%s/data.blob' % (impl[:2], impl),
'guid': impl,
+ 'layer': ['origin'],
+ 'author': {tests.UID: {'name': 'test', 'order': 0, 'role': 3}},
+ 'ctime': self.node_volume['implementation'].get(impl).ctime,
+ 'notes': {'en-us': ''},
+ 'tags': [],
'data': {
'unpack_size': len(activity_info),
'blob_size': len(blob),
@@ -394,8 +399,8 @@ class Implementations(tests.Test):
assert doc.meta('ctime') is not None
assert doc.meta('mtime') is not None
assert doc.meta('seqno') is not None
- self.assertEqual({}, doc.meta('author')['value'])
- self.assertEqual([], doc.meta('layer')['value'])
+ self.assertEqual({tests.UID: {'name': 'test', 'order': 0, 'role': 3}}, doc.meta('author')['value'])
+ self.assertEqual(['origin'], doc.meta('layer')['value'])
self.assertEqual('bundle_id', doc.meta('context')['value'])
self.assertEqual(['Public Domain'], doc.meta('license')['value'])
self.assertEqual('1', doc.meta('version')['value'])
diff --git a/tests/units/client/offline_routes.py b/tests/units/client/offline_routes.py
index 673f6b2..ac7af1b 100755
--- a/tests/units/client/offline_routes.py
+++ b/tests/units/client/offline_routes.py
@@ -104,6 +104,11 @@ class OfflineRoutes(tests.Test):
'stability': 'stable',
'guid': impl1,
'license': ['GPLv3+'],
+ 'layer': ['local'],
+ 'author': {},
+ 'ctime': self.home_volume['implementation'].get(impl1).ctime,
+ 'notes': {'en-us': ''},
+ 'tags': [],
'data': {'spec': {'*-*': {}}},
},
{
@@ -111,6 +116,11 @@ class OfflineRoutes(tests.Test):
'stability': 'stable',
'guid': impl2,
'license': ['GPLv3+'],
+ 'layer': ['local'],
+ 'author': {},
+ 'ctime': self.home_volume['implementation'].get(impl2).ctime,
+ 'notes': {'en-us': ''},
+ 'tags': [],
'data': {
'spec': {'*-*': {
'requires': {
@@ -293,6 +303,11 @@ class OfflineRoutes(tests.Test):
'stability': 'stable',
'version': '1',
'path': tests.tmpdir + '/client/implementation/%s/%s/data.blob' % (impl[:2], impl),
+ 'layer': ['origin'],
+ 'author': {tests.UID: {'name': 'test', 'order': 0, 'role': 3}},
+ 'ctime': self.node_volume['implementation'].get(impl).ctime,
+ 'notes': {'en-us': ''},
+ 'tags': [],
'data': {
'unpack_size': len(activity_info),
'blob_size': len(blob),
@@ -354,6 +369,7 @@ Can't find all required implementations:
'license': 'GPLv3+',
'version': '1',
'stability': 'stable',
+ 'layer': ['origin'],
})
self.home_volume['implementation'].update(impl, {'data': {
'spec': {
@@ -394,6 +410,7 @@ Can't find all required implementations:
'license': 'GPLv3+',
'version': '1',
'stability': 'stable',
+ 'layer': ['origin'],
})
self.home_volume['implementation'].update(impl, {'data': {
'spec': {
@@ -431,6 +448,11 @@ Can't find all required implementations:
'license': ['GPLv3+'],
'stability': 'stable',
'version': '1',
+ 'layer': ['origin', 'local'],
+ 'author': {},
+ 'ctime': self.home_volume['implementation'].get(impl).ctime,
+ 'notes': {'en-us': ''},
+ 'tags': [],
'data': {
'spec': {'*-*': {'commands': {'activity': {'exec': 'true'}}, 'requires': {'dep': {}}}},
},
diff --git a/tests/units/client/online_routes.py b/tests/units/client/online_routes.py
index 46c18c1..ee2efe8 100755
--- a/tests/units/client/online_routes.py
+++ b/tests/units/client/online_routes.py
@@ -187,6 +187,11 @@ class OnlineRoutes(tests.Test):
'stability': 'stable',
'guid': impl1,
'license': ['GPLv3+'],
+ 'layer': ['origin'],
+ 'author': {tests.UID: {'name': 'test', 'order': 0, 'role': 3}},
+ 'ctime': self.node_volume['implementation'].get(impl1).ctime,
+ 'notes': {'en-us': ''},
+ 'tags': [],
'data': {'spec': {'*-*': {}}},
},
{
@@ -194,6 +199,11 @@ class OnlineRoutes(tests.Test):
'stability': 'stable',
'guid': impl2,
'license': ['GPLv3+'],
+ 'layer': ['origin'],
+ 'author': {tests.UID: {'name': 'test', 'order': 0, 'role': 3}},
+ 'ctime': self.node_volume['implementation'].get(impl2).ctime,
+ 'notes': {'en-us': ''},
+ 'tags': [],
'data': {
'spec': {'*-*': {
'requires': {
@@ -440,6 +450,11 @@ Can't find all required implementations:
'stability': 'stable',
'version': '1',
'path': tests.tmpdir + '/client/implementation/%s/%s/data.blob' % (impl[:2], impl),
+ 'layer': ['origin'],
+ 'author': {tests.UID: {'name': 'test', 'order': 0, 'role': 3}},
+ 'ctime': self.node_volume['implementation'].get(impl).ctime,
+ 'notes': {'en-us': ''},
+ 'tags': [],
'data': {
'spec': {
'*-*': {
@@ -491,6 +506,11 @@ Can't find all required implementations:
'license': ['GPLv3+'],
'version': '1',
'stability': 'stable',
+ 'layer': ['origin'],
+ 'author': {tests.UID: {'name': 'test', 'order': 0, 'role': 3}},
+ 'ctime': self.node_volume['implementation'].get(impl).ctime,
+ 'notes': {'en-us': ''},
+ 'tags': [],
'data': {
'foo': 'bar',
'blob_size': len(blob),
@@ -638,6 +658,11 @@ Can't find all required implementations:
'license': ['Public Domain'],
'stability': 'stable',
'version': '1',
+ 'layer': ['origin'],
+ 'author': {tests.UID: {'name': 'test', 'order': 0, 'role': 3}},
+ 'ctime': self.node_volume['implementation'].get(impl).ctime,
+ 'notes': {'en-us': ''},
+ 'tags': [],
'data': {
'unpack_size': len(activity_info),
'blob_size': len(blob),
@@ -846,6 +871,11 @@ Can't find all required implementations:
'stability': 'stable',
'version': '1',
'context': 'bundle_id',
+ 'layer': ['origin'],
+ 'author': {tests.UID: {'name': 'test', 'order': 0, 'role': 3}},
+ 'ctime': self.node_volume['implementation'].get(impl).ctime,
+ 'notes': {'en-us': ''},
+ 'tags': [],
'data': {
'blob_size': len(blob),
'mime_type': 'application/vnd.olpc-sugar',
@@ -866,6 +896,11 @@ Can't find all required implementations:
'stability': 'stable',
'version': '1',
'context': 'bundle_id',
+ 'layer': ['origin'],
+ 'author': {tests.UID: {'name': 'test', 'order': 0, 'role': 3}},
+ 'ctime': self.node_volume['implementation'].get(impl).ctime,
+ 'notes': {'en-us': ''},
+ 'tags': [],
'data': {
'blob': blob_path,
'blob_size': len(blob),
@@ -901,6 +936,11 @@ Can't find all required implementations:
'license': ['Public Domain'],
'stability': 'stable',
'version': '1',
+ 'layer': ['origin'],
+ 'author': {tests.UID: {'name': 'test', 'order': 0, 'role': 3}},
+ 'ctime': self.node_volume['implementation'].get(impl).ctime,
+ 'notes': {'en-us': ''},
+ 'tags': [],
'data': {
'blob_size': len(blob),
'unpack_size': len(activity_info),
@@ -942,6 +982,11 @@ Can't find all required implementations:
'stability': 'stable',
'version': '2',
'path': tests.tmpdir + '/client/implementation/%s/%s/data.blob' % (impl[:2], impl),
+ 'layer': ['origin'],
+ 'author': {tests.UID: {'name': 'test', 'order': 0, 'role': 3}},
+ 'ctime': self.node_volume['implementation'].get(impl).ctime,
+ 'notes': {'en-us': ''},
+ 'tags': [],
'data': {
'blob_size': len(blob),
'unpack_size': len(activity_info),
@@ -1039,6 +1084,11 @@ Can't find all required implementations:
'stability': 'stable',
'version': '1',
'path': tests.tmpdir + '/client/implementation/%s/%s/data.blob' % (impl[:2], impl),
+ 'layer': ['origin'],
+ 'author': {tests.UID: {'name': 'test', 'order': 0, 'role': 3}},
+ 'ctime': self.node_volume['implementation'].get(impl).ctime,
+ 'notes': {'en-us': ''},
+ 'tags': [],
'data': {
'blob_size': len(blob),
'unpack_size': len(activity_info),
@@ -1176,13 +1226,13 @@ Can't find all required implementations:
ipc.get(['context'], reply=['guid', 'layer'], layer='public')['result'])
self.assertEqual(
- [{'guid': impl, 'layer': ['public']}],
+ [{'guid': impl, 'layer': ['origin', 'public']}],
ipc.get(['implementation'], reply=['guid', 'layer'])['result'])
self.assertEqual(
[],
ipc.get(['implementation'], reply=['guid', 'layer'], layer='foo')['result'])
self.assertEqual(
- [{'guid': impl, 'layer': ['public']}],
+ [{'guid': impl, 'layer': ['origin', 'public']}],
ipc.get(['implementation'], reply=['guid', 'layer'], layer='public')['result'])
self.assertEqual({
@@ -1191,6 +1241,11 @@ Can't find all required implementations:
'guid': impl,
'version': '1',
'license': ['GPLv3+'],
+ 'layer': ['origin', 'public'],
+ 'author': {tests.UID: {'name': 'test', 'order': 0, 'role': 3}},
+ 'ctime': self.node_volume['implementation'].get(impl).ctime,
+ 'notes': {'en-us': ''},
+ 'tags': [],
'data': {'spec': {'*-*': {}}},
}],
},
@@ -1205,6 +1260,11 @@ Can't find all required implementations:
'guid': impl,
'version': '1',
'license': ['GPLv3+'],
+ 'layer': ['origin', 'public'],
+ 'author': {tests.UID: {'name': 'test', 'order': 0, 'role': 3}},
+ 'ctime': self.node_volume['implementation'].get(impl).ctime,
+ 'notes': {'en-us': ''},
+ 'tags': [],
'data': {'spec': {'*-*': {}}},
}],
},
@@ -1229,7 +1289,7 @@ Can't find all required implementations:
[],
ipc.get(['implementation'], reply=['guid', 'layer'], layer='foo')['result'])
self.assertEqual(
- [{'guid': impl, 'layer': ['public']}],
+ [{'guid': impl, 'layer': ['origin', 'public']}],
ipc.get(['implementation'], reply=['guid', 'layer'], layer='public')['result'])
self.assertEqual({
@@ -1246,6 +1306,11 @@ Can't find all required implementations:
'guid': impl,
'version': '1',
'license': ['GPLv3+'],
+ 'layer': ['origin', 'public'],
+ 'author': {tests.UID: {'name': 'test', 'order': 0, 'role': 3}},
+ 'ctime': self.node_volume['implementation'].get(impl).ctime,
+ 'notes': {'en-us': ''},
+ 'tags': [],
'data': {'spec': {'*-*': {}}},
}],
},
diff --git a/tests/units/client/solver.py b/tests/units/client/solver.py
index 88ee931..ac2ef52 100755
--- a/tests/units/client/solver.py
+++ b/tests/units/client/solver.py
@@ -107,6 +107,11 @@ class SolverTest(tests.Test):
{'version': '1', 'guid': 'dep2', 'context': 'dep2', 'stability': 'packaged', 'license': None},
{'version': '1', 'guid': 'dep3', 'context': 'dep3', 'stability': 'packaged', 'license': None},
{'version': '1', 'context': context, 'guid': impl, 'stability': 'stable', 'license': ['GPLv3+'],
+ 'layer': ['origin'],
+ 'author': {tests.UID: {'name': 'test', 'order': 0, 'role': 3}},
+ 'ctime': self.node_volume['implementation'].get(impl).ctime,
+ 'notes': {'en-us': ''},
+ 'tags': [],
'data': {'spec': {'*-*': {'commands': {'activity': {'exec': 'echo'}}, 'requires':
{'dep2': {'restrictions': [['1', '2']]}, 'dep3': {}}}}},
'requires': {'dep1': {}, 'dep2': {}}},
@@ -158,7 +163,17 @@ class SolverTest(tests.Test):
},
}})
self.assertEqual([
- {'version': '1', 'context': context, 'guid': impl, 'stability': 'stable', 'license': ['GPLv3+'],
+ {
+ 'version': '1',
+ 'context': context,
+ 'guid': impl,
+ 'stability': 'stable',
+ 'license': ['GPLv3+'],
+ 'layer': ['origin'],
+ 'author': {tests.UID: {'name': 'test', 'order': 0, 'role': 3}},
+ 'ctime': self.node_volume['implementation'].get(impl).ctime,
+ 'notes': {'en-us': ''},
+ 'tags': [],
'data': {'spec': {'*-*': {'commands': {'activity': {'exec': 'echo'}}, 'requires': {'sugar': {}}}}}},
{'version': '0.94', 'context': 'sugar', 'guid': 'sugar-0.94', 'stability': 'packaged', 'license': None},
],
@@ -179,7 +194,17 @@ class SolverTest(tests.Test):
},
}})
self.assertEqual([
- {'version': '1', 'context': context, 'guid': impl, 'stability': 'stable', 'license': ['GPLv3+'],
+ {
+ 'version': '1',
+ 'context': context,
+ 'guid': impl,
+ 'stability': 'stable',
+ 'license': ['GPLv3+'],
+ 'layer': ['origin'],
+ 'author': {tests.UID: {'name': 'test', 'order': 0, 'role': 3}},
+ 'ctime': self.node_volume['implementation'].get(impl).ctime,
+ 'notes': {'en-us': ''},
+ 'tags': [],
'data': {'spec': {'*-*': {'commands': {'activity': {'exec': 'echo'}}, 'requires':
{'sugar': {'restrictions': [['0.80', '0.87']]}}}}}},
{'version': '0.86', 'context': 'sugar', 'guid': 'sugar-0.86', 'stability': 'packaged', 'license': None},
@@ -231,7 +256,17 @@ class SolverTest(tests.Test):
},
}})
self.assertEqual([
- {'version': '1', 'context': context, 'guid': impl, 'stability': 'stable', 'license': ['GPLv3+'],
+ {
+ 'version': '1',
+ 'context': context,
+ 'guid': impl,
+ 'stability': 'stable',
+ 'license': ['GPLv3+'],
+ 'layer': ['origin'],
+ 'author': {tests.UID: {'name': 'test', 'order': 0, 'role': 3}},
+ 'ctime': self.node_volume['implementation'].get(impl).ctime,
+ 'notes': {'en-us': ''},
+ 'tags': [],
'data': {'spec': {'*-*': {'commands': {'activity': {'exec': 'echo'}}, 'requires': {'sugar': {}}}}}},
{'version': '0.94', 'context': 'sugar', 'guid': 'sugar-0.94', 'stability': 'packaged', 'license': None},
],
diff --git a/tests/units/db/resource.py b/tests/units/db/resource.py
index f8a56b1..97e8b1b 100755
--- a/tests/units/db/resource.py
+++ b/tests/units/db/resource.py
@@ -23,7 +23,7 @@ from sugar_network.db import directory as directory_
from sugar_network.db.directory import Directory
from sugar_network.db.index import IndexWriter
from sugar_network.toolkit.router import ACL
-from sugar_network.toolkit import Sequence
+from sugar_network.toolkit import http, Sequence
class ResourceTest(tests.Test):
@@ -332,6 +332,45 @@ class ResourceTest(tests.Test):
json.load(file('%s/%s/prop' % (guid_1[:2], guid_1)))['seqno'],
seqno)
+ def test_patch(self):
+
+ class Document(db.Resource):
+
+ @db.indexed_property(slot=1)
+ def prop1(self, value):
+ return value
+
+ @db.indexed_property(slot=2)
+ def prop2(self, value):
+ return value
+
+ directory = Directory(tests.tmpdir, Document, IndexWriter)
+
+ self.assertRaises(http.NotFound, directory.patch, 'absent', {})
+
+ directory.create({'guid': '1', 'prop1': '1', 'prop2': '2'})
+ self.assertEqual({}, directory.patch('1', {}))
+ self.assertEqual({}, directory.patch('1', {'prop1': '1', 'prop2': '2'}))
+ self.assertEqual({'prop1': '1_'}, directory.patch('1', {'prop1': '1_', 'prop2': '2'}))
+ self.assertEqual({'prop1': '1_', 'prop2': '2_'}, directory.patch('1', {'prop1': '1_', 'prop2': '2_'}))
+
+ def test_patch_LocalizedProps(self):
+
+ class Document(db.Resource):
+
+ @db.indexed_property(slot=1, localized=True)
+ def prop(self, value):
+ return value
+
+ directory = Directory(tests.tmpdir, Document, IndexWriter)
+
+ directory.create({'guid': '1', 'prop': {'ru': 'ru'}})
+ self.assertEqual({}, directory.patch('1', {'prop': 'ru'}))
+ self.assertEqual({'prop': {'ru': 'ru_'}}, directory.patch('1', {'prop': {'ru': 'ru_'}}))
+ self.assertEqual({'prop': {'en': 'en'}}, directory.patch('1', {'prop': {'en': 'en'}}))
+ self.assertEqual({'prop': {'ru': 'ru', 'en': 'en'}}, directory.patch('1', {'prop': {'ru': 'ru', 'en': 'en'}}))
+ self.assertEqual({'prop': {'ru': 'ru_', 'en': 'en'}}, directory.patch('1', {'prop': {'ru': 'ru_', 'en': 'en'}}))
+
def test_diff(self):
class Document(db.Resource):
diff --git a/tests/units/model/implementation.py b/tests/units/model/implementation.py
index ea238c7..6b4bbc3 100755
--- a/tests/units/model/implementation.py
+++ b/tests/units/model/implementation.py
@@ -64,7 +64,7 @@ class ImplementationTest(tests.Test):
xapian.sortable_serialise(eval('1''0000''0000''6''001')),
_fmt_version('1-r1.2-3'))
- def test_WrongAuthor(self):
+ def test_OriginalAuthor(self):
self.start_online_client()
client = IPCConnection()
@@ -77,18 +77,47 @@ class ImplementationTest(tests.Test):
'author': {'fake': None},
})
- impl = {'context': 'context',
- 'license': 'GPLv3+',
- 'version': '1',
- 'stability': 'stable',
- 'notes': '',
- }
- self.assertRaises(http.Forbidden, client.post, ['implementation'], impl)
- self.assertEqual(0, self.node_volume['implementation'].find()[1])
+ guid = client.post(['implementation'], {
+ 'context': 'context',
+ 'license': 'GPLv3+',
+ 'version': '1',
+ 'stability': 'stable',
+ 'notes': '',
+ })
+ self.assertEqual([], self.node_volume['implementation'].get(guid)['layer'])
+
+ guid = client.post(['implementation'], {
+ 'context': 'context',
+ 'license': 'GPLv3+',
+ 'version': '1',
+ 'stability': 'stable',
+ 'notes': '',
+ 'layer': ['foo'],
+ })
+ self.assertEqual(['foo'], self.node_volume['implementation'].get(guid)['layer'])
self.node_volume['context'].update('context', {'author': {tests.UID: None}})
- guid = client.post(['implementation'], impl)
- assert self.node_volume['implementation'].exists(guid)
+
+ guid = client.post(['implementation'], {
+ 'context': 'context',
+ 'license': 'GPLv3+',
+ 'version': '1',
+ 'stability': 'stable',
+ 'notes': '',
+ })
+ self.assertEqual(['origin'], self.node_volume['implementation'].get(guid)['layer'])
+
+ guid = client.post(['implementation'], {
+ 'context': 'context',
+ 'license': 'GPLv3+',
+ 'version': '1',
+ 'stability': 'stable',
+ 'notes': '',
+ 'layer': ['foo'],
+ })
+ self.assertEqual(
+ sorted(['foo', 'origin']),
+ sorted(self.node_volume['implementation'].get(guid)['layer']))
if __name__ == '__main__':
diff --git a/tests/units/node/node.py b/tests/units/node/node.py
index 4f090fc..e019df4 100755
--- a/tests/units/node/node.py
+++ b/tests/units/node/node.py
@@ -646,6 +646,11 @@ class NodeTest(tests.Test):
'guid': impl3,
'version': '3',
'license': ['GPLv3+'],
+ 'layer': ['origin'],
+ 'author': {tests.UID: {'name': tests.UID, 'order': 0, 'role': 3}},
+ 'ctime': self.node_volume['implementation'].get(impl3).ctime,
+ 'notes': {'en-us': ''},
+ 'tags': [],
'data': {
'blob_size': len(blob3),
'spec': {
@@ -724,9 +729,9 @@ class NodeTest(tests.Test):
guid2 = json.load(conn.request('POST', ['implementation'], bundle2, params={'cmd': 'submit'}).raw)
self.assertEqual('1', volume['implementation'].get(guid1)['version'])
- self.assertEqual([], volume['implementation'].get(guid1)['layer'])
+ self.assertEqual(['origin'], volume['implementation'].get(guid1)['layer'])
self.assertEqual('2', volume['implementation'].get(guid2)['version'])
- self.assertEqual([], volume['implementation'].get(guid2)['layer'])
+ self.assertEqual(['origin'], volume['implementation'].get(guid2)['layer'])
self.assertEqual(bundle2, conn.get(['context', 'bundle_id'], cmd='clone'))
activity_info = '\n'.join([
@@ -743,11 +748,11 @@ class NodeTest(tests.Test):
guid3 = json.load(conn.request('POST', ['implementation'], bundle3, params={'cmd': 'submit'}).raw)
self.assertEqual('1', volume['implementation'].get(guid1)['version'])
- self.assertEqual(['deleted'], volume['implementation'].get(guid1)['layer'])
+ self.assertEqual(sorted(['origin', 'deleted']), sorted(volume['implementation'].get(guid1)['layer']))
self.assertEqual('2', volume['implementation'].get(guid2)['version'])
- self.assertEqual([], volume['implementation'].get(guid2)['layer'])
+ self.assertEqual(['origin'], volume['implementation'].get(guid2)['layer'])
self.assertEqual('1', volume['implementation'].get(guid3)['version'])
- self.assertEqual([], volume['implementation'].get(guid3)['layer'])
+ self.assertEqual(['origin'], volume['implementation'].get(guid3)['layer'])
self.assertEqual(bundle2, conn.get(['context', 'bundle_id'], cmd='clone'))
activity_info = '\n'.join([
@@ -764,13 +769,13 @@ class NodeTest(tests.Test):
guid4 = json.load(conn.request('POST', ['implementation'], bundle4, params={'cmd': 'submit'}).raw)
self.assertEqual('1', volume['implementation'].get(guid1)['version'])
- self.assertEqual(['deleted'], volume['implementation'].get(guid1)['layer'])
+ self.assertEqual(sorted(['origin', 'deleted']), sorted(volume['implementation'].get(guid1)['layer']))
self.assertEqual('2', volume['implementation'].get(guid2)['version'])
- self.assertEqual(['deleted'], volume['implementation'].get(guid2)['layer'])
+ self.assertEqual(sorted(['origin', 'deleted']), sorted(volume['implementation'].get(guid2)['layer']))
self.assertEqual('1', volume['implementation'].get(guid3)['version'])
- self.assertEqual([], volume['implementation'].get(guid3)['layer'])
+ self.assertEqual(['origin'], volume['implementation'].get(guid3)['layer'])
self.assertEqual('2', volume['implementation'].get(guid4)['version'])
- self.assertEqual([], volume['implementation'].get(guid4)['layer'])
+ self.assertEqual(['origin'], volume['implementation'].get(guid4)['layer'])
self.assertEqual(bundle3, conn.get(['context', 'bundle_id'], cmd='clone'))
def test_release_UpdateContext(self):
@@ -875,7 +880,7 @@ class NodeTest(tests.Test):
assert context['mtime'] > 0
self.assertEqual({tests.UID: {'role': 3, 'name': 'f470db873b6a35903aca1f2492188e1c4b9ffc42', 'order': 0}}, context['author'])
- def test_release_AuthorsOnly(self):
+ def test_release_ByNonAuthors(self):
volume = self.start_master()
bundle = self.zips(
('ImageViewer.activity/activity/activity.info', '\n'.join([
@@ -893,14 +898,15 @@ class NodeTest(tests.Test):
conn = Connection(auth=http.SugarAuth(join(tests.root, 'data', tests.UID)))
impl1 = json.load(conn.request('POST', ['implementation'], bundle, params={'cmd': 'submit', 'initial': 1}).raw)
impl2 = json.load(conn.request('POST', ['implementation'], bundle, params={'cmd': 'submit'}).raw)
- self.assertEqual(['deleted'], volume['implementation'].get(impl1)['layer'])
- self.assertEqual([], volume['implementation'].get(impl2)['layer'])
+ self.assertEqual(sorted(['origin', 'deleted']), sorted(volume['implementation'].get(impl1)['layer']))
+ self.assertEqual(['origin'], volume['implementation'].get(impl2)['layer'])
conn = Connection(auth=http.SugarAuth(join(tests.root, 'data', tests.UID2)))
conn.get(cmd='whoami')
- self.assertRaises(http.Forbidden, conn.request, 'POST', ['implementation'], bundle, params={'cmd': 'submit'})
- self.assertEqual(['deleted'], volume['implementation'].get(impl1)['layer'])
- self.assertEqual([], volume['implementation'].get(impl2)['layer'])
+ impl3 = json.load(conn.request('POST', ['implementation'], bundle, params={'cmd': 'submit'}).raw)
+ self.assertEqual(sorted(['origin', 'deleted']), sorted(volume['implementation'].get(impl1)['layer']))
+ self.assertEqual(sorted(['origin', 'deleted']), sorted(volume['implementation'].get(impl2)['layer']))
+ self.assertEqual([], volume['implementation'].get(impl3)['layer'])
def call(routes, method, document=None, guid=None, prop=None, principal=None, content=None, **kwargs):