View
@@ -22,23 +22,6 @@ def _mock_annotation(**kwargs):
'Annotation', 'search_lib')
@create_annotation_fixtures
def test_create_annotation_pops_protected_fields(Annotation):
"""It should remove any protected fields before calling Annotation."""
logic.create_annotation(
fields={
'foo': 'bar',
'created': 'foo',
'updated': 'foo',
'user': 'foo',
'id': 'foo'
},
userid='acct:annamaria@example.com')
for field in ('created', 'updated', 'user', 'id'):
assert field not in Annotation.call_args[0][0]
@create_annotation_fixtures
def test_create_annotation_calls_Annotation(Annotation):
fields = mock.MagicMock()
@@ -113,25 +96,6 @@ def test_create_annotation_does_not_crash_if_annotations_parent_has_no_group(
update_annotation_fixtures = pytest.mark.usefixtures('search_lib')
@update_annotation_fixtures
def test_update_annotation_does_not_pass_protected_fields_to_update():
annotation = _mock_annotation()
logic.update_annotation(
annotation,
fields={
'foo': 'bar',
'created': 'foo',
'updated': 'foo',
'user': 'foo',
'id': 'foo'
},
userid='foo')
for field in ('created', 'updated', 'user', 'id'):
assert field not in annotation.update.call_args[0][0]
@update_annotation_fixtures
def test_update_annotation_raises_if_non_admin_changes_perms():
with pytest.raises(RuntimeError):
@@ -191,84 +155,6 @@ def test_update_annotation_user_cannot_change_group():
logic.update_annotation(annotation, fields, 'foo')
@update_annotation_fixtures
def test_update_annotation_removes_userid_from_permissions_if_deleted():
userid = 'acct:fred@hypothes.is'
annotation = _mock_annotation(
deleted=True,
user=userid,
permissions={
'admin': [userid, 'someone else'],
'read': [userid, 'someone else'],
'update': ['someone else'],
'delete': ['someone else']
}
)
fields = {
'permissions': {
'update': [userid],
'delete': [userid]
}
}
logic.update_annotation(annotation, fields, userid)
for action in annotation['permissions']:
assert userid not in annotation['permissions'][action]
@update_annotation_fixtures
def test_update_annotation_does_not_remove_userid_if_not_deleted():
userid = 'acct:fred@hypothes.is'
annotation = _mock_annotation(
deleted=False,
user=userid,
permissions={
'admin': [userid, 'someone else'],
'read': [userid, 'someone else'],
'update': ['someone else'],
'delete': ['someone else']
}
)
fields = {
'permissions': {
'update': [userid],
'delete': [userid]
}
}
logic.update_annotation(annotation, fields, userid)
for action in annotation['permissions']:
assert userid in annotation['permissions'][action]
@update_annotation_fixtures
def test_update_annotation_if_deleted_does_not_remove_other_principals():
userid = 'acct:fred@hypothes.is'
annotation = _mock_annotation(
deleted=True,
user=userid,
permissions={
'admin': [userid, 'someone else'],
'read': [userid, 'someone else'],
'update': [userid],
'delete': [userid]
}
)
fields = {
'permissions': {
'update': ['someone else'],
'delete': ['someone else']
}
}
logic.update_annotation(annotation, fields, userid)
for action in annotation['permissions']:
assert 'someone else' in annotation['permissions'][action]
@update_annotation_fixtures
def test_update_annotation_calls_prepare(search_lib):
annotation = _mock_annotation()
View
@@ -0,0 +1,49 @@
# -*- coding: utf-8 -*-
import pytest
from h.api import schemas
class ExampleSchema(schemas.JSONSchema):
schema = {
'$schema': 'http://json-schema.org/draft-04/schema#',
'type': 'string',
}
def test_jsonschema_returns_data_when_valid():
data = "a string"
assert ExampleSchema().validate(data) == data
def test_jsonschema_raises_when_data_invalid():
data = 123 # not a string
with pytest.raises(schemas.ValidationError):
ExampleSchema().validate(data)
def test_jsonschema_sets_appropriate_error_message_when_data_invalid():
data = 123 # not a string
with pytest.raises(schemas.ValidationError) as e:
ExampleSchema().validate(data)
message = e.value.message
assert message.startswith("123 is not of type 'string'")
@pytest.mark.parametrize('field', [
'created',
'updated',
'user',
'id',
])
def test_annotationschema_removes_protected_fields(field):
data = {}
data[field] = 'something forbidden'
result = schemas.AnnotationSchema().validate(data)
assert field not in result
View

This file was deleted.

Oops, something went wrong.
View
@@ -145,7 +145,7 @@ def test_annotations_index_renders_results(search_lib):
# The fixtures required to mock all of create()'s dependencies.
create_fixtures = pytest.mark.usefixtures(
'logic', 'AnnotationEvent', 'search_lib', 'validators')
'logic', 'AnnotationEvent', 'search_lib', 'schemas')
@create_fixtures
@@ -161,34 +161,35 @@ def test_create_returns_error_if_parsing_json_fails():
@create_fixtures
def test_create_calls_create_annotation(logic):
def test_create_calls_create_annotation(logic, schemas):
"""It should call logic.create_annotation() appropriately."""
request = mock.Mock()
schemas.AnnotationSchema.return_value.validate.return_value = {'foo': 123}
views.create(request)
logic.create_annotation.assert_called_once_with(
request.json_body,
{'foo': 123},
userid=request.authenticated_userid)
@create_fixtures
def test_create_calls_validator(validators):
def test_create_calls_validator(schemas):
request = mock.Mock()
views.create(request)
validators.Annotation.return_value.validate.assert_called_once_with(
schemas.AnnotationSchema.return_value.validate.assert_called_once_with(
request.json_body)
@create_fixtures
def test_create_returns_api_error_for_validation_error(validators):
class Error(Exception):
def test_create_returns_api_error_for_validation_error(schemas):
class ValidationError(Exception):
pass
validators.Error = Error
validators.Annotation.return_value.validate.side_effect = (
validators.Error(mock.sentinel.reason))
schemas.ValidationError = ValidationError
schemas.AnnotationSchema.return_value.validate.side_effect = (
schemas.ValidationError(mock.sentinel.reason))
response = views.create(mock.Mock())
@@ -274,7 +275,7 @@ def test_read_does_not_crash_if_annotation_has_no_group():
# The fixtures required to mock all of update()'s dependencies.
update_fixtures = pytest.mark.usefixtures(
'logic', 'Annotation', 'search_lib', 'validators')
'logic', 'Annotation', 'search_lib', 'schemas')
@update_fixtures
@@ -289,22 +290,22 @@ def test_update_returns_error_if_json_parsing_fails():
@update_fixtures
def test_update_calls_validator(validators):
def test_update_calls_validator(schemas):
request = mock.Mock()
views.update(mock.Mock(), request)
validators.Annotation.return_value.validate.assert_called_once_with(
schemas.AnnotationSchema.return_value.validate.assert_called_once_with(
request.json_body)
@update_fixtures
def test_update_returns_api_error_for_validation_error(validators):
class Error(Exception):
def test_update_returns_api_error_for_validation_error(schemas):
class ValidationError(Exception):
pass
validators.Error = Error
validators.Annotation.return_value.validate.side_effect = (
validators.Error(mock.sentinel.reason))
schemas.ValidationError = ValidationError
schemas.AnnotationSchema.return_value.validate.side_effect = (
schemas.ValidationError(mock.sentinel.reason))
response = views.update(mock.Mock(), mock.Mock())
@@ -313,15 +314,16 @@ class Error(Exception):
@update_fixtures
def test_update_calls_update_annotation(logic):
def test_update_calls_update_annotation(logic, schemas):
context = mock.Mock()
request = mock.Mock()
schemas.AnnotationSchema.return_value.validate.return_value = {'foo': 123}
views.update(context, request)
logic.update_annotation.assert_called_once_with(
context.model,
request.json_body,
{'foo': 123},
request.authenticated_userid)
@@ -468,7 +470,7 @@ def access_token(request):
@pytest.fixture
def validators(request):
patcher = mock.patch('h.api.views.validators', autospec=True)
def schemas(request):
patcher = mock.patch('h.api.views.schemas', autospec=True)
request.addfinalizer(patcher.stop)
return patcher.start()
View

This file was deleted.

Oops, something went wrong.
View
@@ -12,7 +12,7 @@
from h.api.events import AnnotationEvent
from h.api import search as search_lib
from h.api import logic
from h.api import validators
from h.api import schemas
from h.api.resources import Annotation
from h.api.resources import Annotations
from h.api.resources import Root
@@ -158,11 +158,11 @@ def create(request):
status_code=400) # Client Error: Bad Request
try:
validators.Annotation().validate(fields)
except validators.Error as err:
appstruct = schemas.AnnotationSchema().validate(fields)
except schemas.ValidationError as err:
return _api_error(request, err.message, status_code=400)
annotation = logic.create_annotation(fields,
annotation = logic.create_annotation(appstruct,
userid=request.authenticated_userid)
# Notify any subscribers
@@ -197,14 +197,14 @@ def update(context, request):
status_code=400) # Client Error: Bad Request
try:
validators.Annotation().validate(fields)
except validators.Error as err:
appstruct = schemas.AnnotationSchema().validate(fields)
except schemas.ValidationError as err:
return _api_error(request, err.message, status_code=400)
# Update and store the annotation
try:
logic.update_annotation(annotation,
fields,
appstruct,
userid=request.authenticated_userid)
except RuntimeError as err:
return _api_error(
View
@@ -3,7 +3,7 @@
events = require('../events')
# Validate an annotation.
# Annotations must be attributed to a user or marked as deleted.
# Annotations must be attributed to a user.
# A public annotation is valid only if they have a body.
# A non-public annotation requires only a target (e.g. a highlight).
validate = (value) ->
@@ -101,7 +101,7 @@ AnnotationController = [
###
this.isHighlight = ->
model.target?.length and not model.references?.length and
not (model.text or model.deleted or model.tags?.length)
not (model.text or model.tags?.length)
###*
# @ngdoc method
@@ -253,7 +253,7 @@ AnnotationController = [
# @description Saves any edits and returns to the viewer.
###
this.save = ->
unless model.user or model.deleted
unless model.user
return flash.info('Please sign in to save your annotations.')
unless validate(@annotation)
return flash.info('Please add text or a tag before publishing.')
View
@@ -4,9 +4,6 @@
<div ng-if="vm.annotation.user">
<header class="annotation-header">
<!-- Deletion notice -->
<div ng-if="!vm.editing && vm.annotation.deleted">Annotation deleted.</div>
<!-- User -->
<span ng-if="vm.annotation.user">
<span>
View
@@ -46,7 +46,7 @@ def run_tests(self):
'gnsq>=0.3.0,<0.4.0',
'gunicorn>=19.2,<20',
'jsonpointer==1.0',
'jsonschema==1.3.0',
'jsonschema>=2.5.1,<2.6',
'oauthlib==0.6.3',
'pyramid>=1.5,<1.6',
'psycogreen>=1.0',