diff --git a/flask_potion/__init__.py b/flask_potion/__init__.py index 8e487e9..a00e413 100644 --- a/flask_potion/__init__.py +++ b/flask_potion/__init__.py @@ -5,7 +5,7 @@ from flask import current_app, make_response, json, Response, request from six import wraps from werkzeug.exceptions import HTTPException -from werkzeug.wrappers import BaseResponse +from werkzeug.wrappers import Response from .exceptions import PotionException from .routes import RouteSet, to_camel_case from .utils import unpack @@ -156,7 +156,7 @@ def output(self, view): def wrapper(*args, **kwargs): resp = view(*args, **kwargs) - if isinstance(resp, BaseResponse): + if isinstance(resp, Response): return resp data, code, headers = unpack(resp) diff --git a/flask_potion/contrib/alchemy/manager.py b/flask_potion/contrib/alchemy/manager.py index 6aaaa40..8d8f949 100644 --- a/flask_potion/contrib/alchemy/manager.py +++ b/flask_potion/contrib/alchemy/manager.py @@ -1,5 +1,5 @@ from flask import current_app -from flask_sqlalchemy import Pagination as SAPagination, get_state +from flask_sqlalchemy.pagination import Pagination as SAPagination from sqlalchemy import String, or_, and_ from sqlalchemy.dialects import postgresql from sqlalchemy.exc import IntegrityError @@ -9,7 +9,7 @@ from sqlalchemy.orm.exc import NoResultFound from flask_potion import fields -from flask_potion.contrib.alchemy.filters import FILTER_NAMES, FILTERS_BY_TYPE, SQLAlchemyBaseFilter +from flask_potion.contrib.alchemy.filters import FILTER_NAMES, FILTERS_BY_TYPE from flask_potion.exceptions import ItemNotFound, DuplicateKey, BackendConflict from flask_potion.instances import Pagination from flask_potion.manager import RelationalManager @@ -144,7 +144,7 @@ def _is_sortable_field(self, field): @staticmethod def _get_session(): - return get_state(current_app).db.session + return current_app.extensions['sqlalchemy'].session @staticmethod def _is_change(a, b): diff --git a/flask_potion/contrib/principals/permission.py b/flask_potion/contrib/principals/permission.py index 722dfd3..a590d8d 100644 --- a/flask_potion/contrib/principals/permission.py +++ b/flask_potion/contrib/principals/permission.py @@ -1,6 +1,5 @@ from flask import g from flask_principal import Permission -from sqlalchemy import or_ from .needs import HybridNeed diff --git a/flask_potion/reference.py b/flask_potion/reference.py index 393098b..2a95a10 100644 --- a/flask_potion/reference.py +++ b/flask_potion/reference.py @@ -2,7 +2,6 @@ import inspect import six -from flask import current_app, _app_ctx_stack class ResourceReference(object): diff --git a/flask_potion/routes.py b/flask_potion/routes.py index 9c16a87..87e5526 100644 --- a/flask_potion/routes.py +++ b/flask_potion/routes.py @@ -4,6 +4,7 @@ from types import MethodType from flask import request +from sqlalchemy.orm.exc import StaleDataError from werkzeug.utils import cached_property from flask_potion.reference import _bind_schema @@ -444,7 +445,10 @@ def relation_add(resource, item, target_item): def relation_remove(resource, item, target_id): target_item = self.target.manager.read(target_id) resource.manager.relation_remove(item, self.attribute, self.target, target_item) - resource.manager.commit() + try: + resource.manager.commit() + except StaleDataError: + pass return None, 204 yield relation_route.for_method('DELETE', diff --git a/flask_potion/schema.py b/flask_potion/schema.py index f960f84..94ed089 100644 --- a/flask_potion/schema.py +++ b/flask_potion/schema.py @@ -130,6 +130,7 @@ def __init__(self, schema): def schema(self): return self._schema + class FieldSet(Schema, ResourceBound): """ A schema representation of a dictionary of :class:`fields.Raw` objects. @@ -264,7 +265,9 @@ def parse_request(self, request): if not self.all_fields_optional: raise RequestMustBeJSON() - data = request.get_json(silent=False) + data = None + if request.method != "GET": + data = request.get_json(silent=False) if data is None and self.all_fields_optional: data = {} diff --git a/flask_potion/utils.py b/flask_potion/utils.py index cad52b1..f0dbae9 100644 --- a/flask_potion/utils.py +++ b/flask_potion/utils.py @@ -1,29 +1,18 @@ -from flask import _app_ctx_stack, _request_ctx_stack +from urllib.parse import urlsplit + +from flask import current_app, request from werkzeug.exceptions import NotFound -from werkzeug.urls import url_parse def route_from(url, method=None): - # Source: http://stackoverflow.com/questions/19129407/looking-for-inverse-of-url-for-in-flask - appctx = _app_ctx_stack.top - reqctx = _request_ctx_stack.top - if appctx is None: - raise RuntimeError('Attempted to match a URL without the ' - 'application context being pushed. This has to be ' - 'executed when application context is available.') + environ = dict(request.environ) + url_request = current_app.request_class(environ) + url_adapter = current_app.create_url_adapter(url_request) + parsed_url = urlsplit(url) - if reqctx is not None: - url_adapter = reqctx.url_adapter - else: - url_adapter = appctx.url_adapter - if url_adapter is None: - raise RuntimeError('Application was not able to create a URL ' - 'adapter for request independent URL matching. ' - 'You might be able to fix this by setting ' - 'the SERVER_NAME config variable.') - parsed_url = url_parse(url) - if parsed_url.netloc is not "" and parsed_url.netloc != url_adapter.server_name: + if parsed_url.netloc != "" and parsed_url.netloc != url_adapter.server_name: raise NotFound() + return url_adapter.match(parsed_url.path, method) diff --git a/setup.py b/setup.py index d0017af..6923bc5 100644 --- a/setup.py +++ b/setup.py @@ -7,10 +7,7 @@ tests_require = [ 'Flask-Testing>=0.4.1', 'Flask-Principal>=0.4.0', - 'Flask-SQLAlchemy>=2.0', - 'Flask-MongoEngine>=0.7.1', - 'peewee==2.*', - 'nose>=1.1.2', + 'Flask-SQLAlchemy>=3.0', ] setup( @@ -26,7 +23,7 @@ test_suite='nose.collector', tests_require=tests_require, install_requires=[ - 'Flask>=0.10', + 'Flask>=2.3.0', 'jsonschema>=2.4.0', 'aniso8601>=0.84', 'blinker>=1.3', @@ -51,19 +48,13 @@ ], zip_safe=False, extras_require={ - 'docs': ['sphinx', 'Flask-Principal', 'Flask-SQLAlchemy', 'peewee'], + 'docs': ['sphinx', 'Flask-Principal', 'Flask-SQLAlchemy'], 'principal': [ 'Flask-Principal', ], 'sqlalchemy': [ 'Flask-SQLAlchemy>=2.0' ], - 'peewee': [ - 'peewee==2.*' - ], - 'mongoengine': [ - 'Flask-MongoEngine>=0.7.0' - ], 'tests': tests_require, } ) diff --git a/tests/__init__.py b/tests/__init__.py index 05870ea..303d645 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -22,7 +22,6 @@ def open(self, *args, **kw): class BaseTestCase(TestCase): - def assertJSONEqual(self, first, second, msg=None): self.assertEqual(json.loads(json.dumps(first)), json.loads(json.dumps(second)), msg) diff --git a/tests/contrib/alchemy/test_filter.py b/tests/contrib/alchemy/test_filter.py index b91f792..c3c22e7 100644 --- a/tests/contrib/alchemy/test_filter.py +++ b/tests/contrib/alchemy/test_filter.py @@ -15,6 +15,7 @@ def setUp(self): super(FilterTestCase, self).setUp() app = self.app app.config['SQLALCHEMY_ENGINE'] = 'sqlite://' + app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://' app.config['TESTING'] = True self.api = Api(self.app) @@ -44,7 +45,6 @@ class Thing(sa.Model): sa.create_all() - class UserResource(ModelResource): class Schema: gender = fields.String(enum=['f', 'm'], nullable=True) diff --git a/tests/contrib/alchemy/test_manager_sqlalchemy.py b/tests/contrib/alchemy/test_manager_sqlalchemy.py index 47c3e0c..75284e7 100644 --- a/tests/contrib/alchemy/test_manager_sqlalchemy.py +++ b/tests/contrib/alchemy/test_manager_sqlalchemy.py @@ -12,6 +12,7 @@ class SQLAlchemyTestCase(BaseTestCase): def setUp(self): super(SQLAlchemyTestCase, self).setUp() self.app.config['SQLALCHEMY_ENGINE'] = 'sqlite://' + self.app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://' self.api = Api(self.app, default_manager=SQLAlchemyManager) self.sa = sa = SQLAlchemy(self.app, session_options={"autoflush": False}) @@ -64,10 +65,6 @@ def test_field_discovery(self): self.assertEqual(self.MachineResource.meta.name, 'machine') self.assertEqual(self.TypeResource.meta.name, 'type') - def test_create_no_json(self): - response = self.client.post('/machine', data='invalid') - self.assert400(response) - def test_create_json_string(self): response = self.client.post('/machine', data='invalid', force_json=True) self.assert400(response) @@ -193,7 +190,7 @@ def test_update(self): "additionalProperties": False, "properties": { "$ref": { - "pattern": "^\\/type\\/[^/]+$", + "pattern": "^/type\\/[^/]+$", "type": "string" } }, @@ -233,6 +230,7 @@ class SQLAlchemyRelationTestCase(BaseTestCase): def setUp(self): super(SQLAlchemyRelationTestCase, self).setUp() self.app.config['SQLALCHEMY_ENGINE'] = 'sqlite://' + self.app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://' self.api = Api(self.app) self.sa = sa = SQLAlchemy(self.app, session_options={"autoflush": False}) @@ -375,6 +373,7 @@ class SQLAlchemyInspectionTestCase(BaseTestCase): def setUp(self): super(SQLAlchemyInspectionTestCase, self).setUp() self.app.config['SQLALCHEMY_ENGINE'] = 'sqlite://' + self.app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://' self.api = Api(self.app) self.sa = sa = SQLAlchemy(self.app, session_options={"autoflush": False}) @@ -417,6 +416,7 @@ class SQLAlchemySequenceTestCase(BaseTestCase): def setUp(self): super(SQLAlchemySequenceTestCase, self).setUp() self.app.config['SQLALCHEMY_ENGINE'] = 'sqlite://' + self.app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://' self.api = Api(self.app) self.sa = sa = SQLAlchemy(self.app, session_options={"autoflush": False}) @@ -455,6 +455,7 @@ class SQLAlchemySortTestCase(BaseTestCase): def setUp(self): super(SQLAlchemySortTestCase, self).setUp() self.app.config['SQLALCHEMY_ENGINE'] = 'sqlite://' + self.app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://' self.api = Api(self.app) self.sa = sa = SQLAlchemy( self.app, session_options={"autoflush": False}) @@ -529,6 +530,7 @@ class QueryOptionsSQLAlchemyTestCase(BaseTestCase): def setUp(self): super(QueryOptionsSQLAlchemyTestCase, self).setUp() self.app.config['SQLALCHEMY_ENGINE'] = 'sqlite://' + self.app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://' self.api = Api(self.app, default_manager=SQLAlchemyManager) self.sa = sa = SQLAlchemy(self.app, session_options={"autoflush": False}) diff --git a/tests/contrib/alchemy/test_signals.py b/tests/contrib/alchemy/test_signals.py index 47bcb1d..09bd4fe 100644 --- a/tests/contrib/alchemy/test_signals.py +++ b/tests/contrib/alchemy/test_signals.py @@ -1,4 +1,5 @@ from functools import partial +from contextlib import contextmanager from flask_sqlalchemy import SQLAlchemy from sqlalchemy.orm import backref from flask_potion import fields @@ -7,7 +8,6 @@ from flask_potion.resource import ModelResource from flask_potion import Api from tests import BaseTestCase -from blinker._utilities import contextmanager from blinker import ANY @@ -15,6 +15,7 @@ class SQLAlchemySignalTestCase(BaseTestCase): def setUp(self): super(SQLAlchemySignalTestCase, self).setUp() self.app.config['SQLALCHEMY_ENGINE'] = 'sqlite://' + self.app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://' self.api = Api(self.app) self.sa = sa = SQLAlchemy(self.app) diff --git a/tests/contrib/mongoengine/__init__.py b/tests/contrib/mongoengine/__init__.py deleted file mode 100644 index 8e3020f..0000000 --- a/tests/contrib/mongoengine/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -from flask_mongoengine import MongoEngine -from pymongo.database import Database - -from flask_potion import Api -from flask_potion.contrib.mongoengine import MongoEngineManager -from tests import BaseTestCase - - -class MongoEngineTestCase(BaseTestCase): - def setUp(self): - super(MongoEngineTestCase, self).setUp() - self.app.config['MONGODB_DB'] = 'potion-test-db' - self.api = Api(self.app, default_manager=MongoEngineManager) - self.me = me = MongoEngine(self.app) - - def tearDown(self): - connection_or_db = self.me.connection - - # MongoEngine.connection value changed in flask-mongoengine 0.8 - if isinstance(connection_or_db, Database): - connection_or_db.client.drop_database(connection_or_db) - else: - connection_or_db.drop_database('potion-test-db') diff --git a/tests/contrib/mongoengine/test_fields.py b/tests/contrib/mongoengine/test_fields.py deleted file mode 100644 index f6bb438..0000000 --- a/tests/contrib/mongoengine/test_fields.py +++ /dev/null @@ -1,88 +0,0 @@ -from bson import ObjectId -from bson.errors import InvalidId -from mongoengine import ObjectIdField, MapField, FloatField, StringField, EmbeddedDocumentField, \ - EmbeddedDocument - -from flask_potion import Api, ModelResource -from tests.contrib.mongoengine import MongoEngineTestCase - - -class FieldsTestCase(MongoEngineTestCase): - def test_object_id_field(self): - - class Model(self.me.Document): - meta = { - 'collection': 'model' - } - - an_id = ObjectIdField() - - class Resource(ModelResource): - class Meta: - model = Model - - self.api.add_resource(Resource) - - response = self.client.get('/model/schema') - self.assertEqual(response.json['properties']['an_id'], {'type': 'string'}) - self.assertRaises(InvalidId, self.client.post, '/model', data={'an_id': "abc"}) - - hex = 'adfb48a6763c5741b92f6ade' - object_id = ObjectId('adfb48a6763c5741b92f6ade') - response = self.client.post('/model', data={'an_id': hex}) - self.assertEqual(object_id, ObjectId(response.json['an_id'])) - - def test_map_field(self): - - class ReferenceModel(EmbeddedDocument): - id = ObjectIdField() - meta = { - 'collection': 'reference-model' - } - - class Model(self.me.Document): - meta = { - 'collection': 'model' - } - - float_mapping = MapField(FloatField()) - string_mapping = MapField(StringField()) - object_id_mapping = MapField(ObjectIdField()) - reference_mapping = MapField(EmbeddedDocumentField(ReferenceModel)) - - class Resource(ModelResource): - class Meta: - model = Model - - self.api.add_resource(Resource) - - response = self.client.get('/model/schema') - self.assertEqual(response.json['properties']['float_mapping']['additionalProperties'], {'type': 'number'}) - self.assertEqual(response.json['properties']['float_mapping']['default'], {}) - self.assertEqual(response.json['properties']['string_mapping']['additionalProperties'], {'type': 'string'}) - self.assertEqual(response.json['properties']['string_mapping']['default'], {}) - self.assertEqual(response.json['properties']['reference_mapping']['additionalProperties'], - { - 'additionalProperties': False, - 'type': 'object', - 'properties': { - 'id': {'type': 'string'} - } - }) - self.assertEqual(response.json['properties']['reference_mapping']['default'], {}) - ref_model = dict(id='adfb48a6763c5741b92f6ade') - response = self.client.post("/model", data={'reference_mapping': {"1": ref_model}, 'object_id_mapping': {"a": ref_model["id"]}}) - self.assertEqualWithout(response.json, - { - 'reference_mapping': { - '1': { - 'id': 'adfb48a6763c5741b92f6ade' - } - }, - '$uri': '/model/561e6a807f551ce7d5df1003', - 'float_mapping': {}, - 'object_id_mapping': { - 'a': 'adfb48a6763c5741b92f6ade' - }, 'string_mapping': {} - }, - without=["$uri"]) \ No newline at end of file diff --git a/tests/contrib/mongoengine/test_filter.py b/tests/contrib/mongoengine/test_filter.py deleted file mode 100644 index cc3d28f..0000000 --- a/tests/contrib/mongoengine/test_filter.py +++ /dev/null @@ -1,205 +0,0 @@ -import unittest - -from flask_mongoengine import MongoEngine -from mongoengine.fields import IntField, StringField, BooleanField - -from flask_potion.contrib.mongoengine import MongoEngineManager -from flask_potion import ModelResource, fields, Api -from tests.contrib.mongoengine import MongoEngineTestCase - - -class FilterTestCase(MongoEngineTestCase): - def setUp(self): - super(FilterTestCase, self).setUp() - - class User(self.me.Document): - meta = { - "collection": "user" - } - - first_name = StringField(max_length=60, null=False) - last_name = StringField(max_length=60, null=False) - - gender = StringField(max_length=1) - - age = IntField() - - is_staff = BooleanField(default=None) - - class UserResource(ModelResource): - class Schema: - gender = fields.String(enum=['f', 'm'], nullable=True) - - class Meta: - model = User - - class AllowUserResource(ModelResource): - class Meta: - model = User - name = 'allow-user' - filters = { - 'first_name': ['eq'], - 'is_staff': True - } - - self.api.add_resource(UserResource) - self.api.add_resource(AllowUserResource) - - def post_sample_set_a(self): - for user in [ - {'first_name': 'John', 'last_name': 'Doe', 'age': 32, 'is_staff': True, 'gender': 'm'}, - {'first_name': 'Jonnie', 'last_name': 'Doe', 'age': 25, 'is_staff': False, 'gender': 'm'}, - {'first_name': 'Jane', 'last_name': 'Roe', 'age': 18, 'is_staff': False, 'gender': 'f'}, - {'first_name': 'Joe', 'last_name': 'Bloggs', 'age': 21, 'is_staff': True, 'gender': 'm'}, - {'first_name': 'Sue', 'last_name': 'Watts', 'age': 25, 'is_staff': True} - ]: - response = self.client.post('/user', data=user) - self.assert200(response) - - def test_equality(self): - self.post_sample_set_a() - - response = self.client.get('/user?where={"last_name": "Doe"}') - - self.assertEqualWithout([ - {'first_name': 'John', 'last_name': 'Doe'}, - {'first_name': 'Jonnie', 'last_name': 'Doe'}, - ], response.json, without=['$uri', '$uri', '$id', '$type', 'gender', 'age', 'is_staff']) - - response = self.client.get('/user?where={"age": 25}') - - self.assertEqualWithout([ - {'first_name': 'Jonnie', 'last_name': 'Doe', 'age': 25}, - {'first_name': 'Sue', 'last_name': 'Watts', 'age': 25}, - ], response.json, without=['$uri', '$id', '$type', 'gender', 'is_staff']) - - response = self.client.get('/user?where={"last_name": "Doe", "age": 25}') - - self.assertEqualWithout([ - {'first_name': 'Jonnie', 'last_name': 'Doe'}, - ], response.json, without=['$uri', '$id', '$type', 'gender', 'age', 'is_staff']) - - def test_inequality(self): - self.post_sample_set_a() - - response = self.client.get('/user?where={"last_name": {"$ne": "Watts"}, "age": {"$ne": 32}}') - - self.assertEqualWithout([ - {'first_name': 'Jonnie', 'last_name': 'Doe'}, - {'first_name': 'Jane', 'last_name': 'Roe'}, - {'first_name': 'Joe', 'last_name': 'Bloggs'}, - ], response.json, without=['$uri', '$id', '$type', 'gender', 'age', 'is_staff']) - - response = self.client.get('/user?where={"age": {"$gt": 25}}') - - self.assertEqualWithout([ - {'first_name': 'John', 'last_name': 'Doe'} - ], response.json, without=['$uri', '$id', '$type', 'gender', 'age', 'is_staff']) - - response = self.client.get('/user?where={"age": {"$gte": 25}}') - - self.assertEqualWithout([ - {'first_name': 'John', 'last_name': 'Doe'}, - {'first_name': 'Jonnie', 'last_name': 'Doe'}, - {'first_name': 'Sue', 'last_name': 'Watts'}, - ], response.json, without=['$uri', '$id', '$type', 'gender', 'age', 'is_staff']) - - response = self.client.get('/user?where={"age": {"$lte": 21}}') - - self.assertEqualWithout([ - {'first_name': 'Jane', 'last_name': 'Roe'}, - {'first_name': 'Joe', 'last_name': 'Bloggs'} - ], response.json, without=['$uri', '$id', '$type', 'gender', 'age', 'is_staff']) - - response = self.client.get('/user?where={"age": {"$lt": null}}') - self.assert400(response) - - response = self.client.get('/user?where={"first_name": {"$gt": "Jo"}}') - self.assert400(response) - - def test_in(self): - self.post_sample_set_a() - - response = self.client.get('/user?where={"last_name": {"$in": ["Bloggs", "Watts"]}}') - - self.assertEqualWithout([ - {'first_name': 'Joe', 'last_name': 'Bloggs'}, - {'first_name': 'Sue', 'last_name': 'Watts'} - ], response.json, without=['$uri', '$id', '$type', 'gender', 'age', 'is_staff']) - - response = self.client.get('/user?where={"last_name": {"$in": []}}') - - self.assertEqualWithout([], response.json, without=['$uri', '$id', '$type', 'gender', 'age', 'is_staff']) - - def test_startswith(self): - self.post_sample_set_a() - - response = self.client.get('/user?where={"first_name": {"$startswith": "Jo"}}') - - self.assertEqualWithout([ - {'first_name': 'John', 'last_name': 'Doe'}, - {'first_name': 'Jonnie', 'last_name': 'Doe'}, - {'first_name': 'Joe', 'last_name': 'Bloggs'} - ], response.json, without=['$uri', '$id', '$type', 'gender', 'age', 'is_staff']) - - response = self.client.get('/user?where={"first_name": {"$startswith": "J%e"}}') - - self.assertEqualWithout([], response.json, without=['$uri', '$id', '$type', 'gender', 'age', 'is_staff']) - - @unittest.SkipTest - def test_text_search(self): - self.post_sample_set_a() - response = self.client.get('/user?search=sbc+dedf&rank=1') - - def test_sort(self): - self.post_sample_set_a() - - response = self.client.get('/user?sort={"last_name": true, "first_name": false}') - - self.assert200(response) - self.assertEqualWithout([ - {'first_name': 'Sue', 'last_name': 'Watts'}, - {'first_name': 'Jane', 'last_name': 'Roe'}, - {'first_name': 'John', 'last_name': 'Doe'}, - {'first_name': 'Jonnie', 'last_name': 'Doe'}, - {'first_name': 'Joe', 'last_name': 'Bloggs'} - ], response.json, - without=['$uri', '$id', '$type', 'gender', 'age', 'is_staff']) - - response = self.client.get('/user?sort={"age": false, "first_name": false}') - - self.assertEqualWithout([ - {'first_name': 'Jane', 'last_name': 'Roe'}, - {'first_name': 'Joe', 'last_name': 'Bloggs'}, - {'first_name': 'Jonnie', 'last_name': 'Doe'}, - {'first_name': 'Sue', 'last_name': 'Watts'}, - {'first_name': 'John', 'last_name': 'Doe'}, - ], response.json, - without=['$uri', '$id', '$type', 'gender', 'age', 'is_staff']) - - - def test_sort_and_where(self): - self.post_sample_set_a() - - response = self.client.get('/user?where={"first_name": {"$startswith": "Jo"}}&sort={"first_name": false}') - - self.assertEqualWithout([ - {'first_name': 'Joe', 'last_name': 'Bloggs'}, - {'first_name': 'John', 'last_name': 'Doe'}, - {'first_name': 'Jonnie', 'last_name': 'Doe'} - ], response.json, without=['$uri', '$id', '$type', 'gender', 'age', 'is_staff']) - - @unittest.SkipTest - def test_sort_pages(self): - pass - - @unittest.SkipTest - def test_disallowed_where_filters(self): - pass - - @unittest.SkipTest - def test_schema(self): - pass - -if __name__ == '__main__': - unittest.main() \ No newline at end of file diff --git a/tests/contrib/mongoengine/test_manager_mongoengine.py b/tests/contrib/mongoengine/test_manager_mongoengine.py deleted file mode 100644 index 0ab0509..0000000 --- a/tests/contrib/mongoengine/test_manager_mongoengine.py +++ /dev/null @@ -1,377 +0,0 @@ -import unittest - -from flask_mongoengine import MongoEngine -from mongoengine.fields import StringField, FloatField, ReferenceField, ListField - -from flask_potion.contrib.mongoengine import MongoEngineManager -from flask_potion.routes import Relation -from flask_potion import Api, fields -from flask_potion.resource import ModelResource -from tests import BaseTestCase -from tests.contrib.mongoengine import MongoEngineTestCase - - -class MongoEngineManagerTestCase(MongoEngineTestCase): - def setUp(self): - super(MongoEngineManagerTestCase, self).setUp() - - class Type(self.me.Document): - meta = {"collection": "type"} - name = StringField(max_length=60, null=False, unique=True) - - @property - def machines(self): - return Machine.objects(type=self) - - @machines.setter - def machines(self, machines): - for m in machines: - m.type = self - - class Machine(self.me.Document): - meta = {"collection": "machine"} - name = StringField(max_length=60, null=False) - - wattage = FloatField(null=True) - - type = ReferenceField(Type) - - class MachineResource(ModelResource): - class Meta: - model = Machine - include_id = True - include_type = True - - class Schema: - type = fields.ToOne('type') - - class TypeResource(ModelResource): - class Meta: - model = Type - include_id = True - include_type = True - - class Schema: - machines = fields.ToMany('machine') - - self.MachineResource = MachineResource - self.TypeResource = TypeResource - - self.api.add_resource(MachineResource) - self.api.add_resource(TypeResource) - - def test_field_discovery(self): - self.assertEqual(set(self.MachineResource.schema.fields.keys()), {'$id', '$type', 'name', 'type', 'wattage'}) - self.assertEqual(set(self.TypeResource.schema.fields.keys()), {'$id', '$type', 'name', 'machines'}) - self.assertEqual(self.MachineResource.meta.name, 'machine') - self.assertEqual(self.TypeResource.meta.name, 'type') - - def test_create_no_json(self): - response = self.client.post('/machine', data='invalid') - self.assert400(response) - - def test_create_json_string(self): - response = self.client.post('/machine', data='invalid', force_json=True) - self.assert400(response) - - def test_conflict(self): - response = self.client.post('/type', data={"name": "foo"}) - self.assert200(response) - - response = self.client.post('/type', data={"name": "foo"}) - self.assertStatus(response, 409) - - def test_create(self): - response = self.client.post('/type', data={}) - self.assert400(response) - self.assertEqual({ - "errors": [ - { - "message": "'name' is a required property", - "path": [], - "validationOf": { - "required": [ - "name" - ] - } - } - ], - "message": "Bad Request", - "status": 400 - }, response.json) - - response = self.client.post('/type', data={"name": "x-ray"}) - type_id = response.json["$id"] - self.assertEqualWithout({'$type': 'type', 'machines': [], "name": "x-ray"}, response.json, without=["$id"]) - - response = self.client.post('/machine', - data={"name": "Irradiator I", "type": {"$ref": "/type/{}".format(type_id)}}) - machine1_id = response.json["$id"] - self.assert200(response) - self.assertEqualWithout( - {'$type': 'machine', 'type': {"$ref": "/type/{}".format(type_id)}, "wattage": None, "name": "Irradiator I"}, - response.json, - without=["$id"]) - - response = self.client.post('/machine', data={"name": "Sol IV", "type": type_id, "wattage": 1.23e45}) - machine2_id = response.json["$id"] - self.assert200(response) - self.assertEqualWithout( - {'$type': 'machine', 'type': {"$ref": "/type/{}".format(type_id)}, "wattage": 1.23e45, "name": "Sol IV"}, - response.json, - without=["$id"]) - - response = self.client.get('/type/{}'.format(type_id)) - self.assert200(response) - self.assertEqualWithout( - {'$type': 'type', - 'machines': [ - {'$ref': '/machine/{}'.format(machine1_id)}, - {'$ref': '/machine/{}'.format(machine2_id)} - ], - "name": "x-ray"}, - response.json, - without=["$id"]) - - def test_get(self): - type_ = lambda v: {"$type": "type", "name": "Type-{}".format(v), "machines": []} - - for i in range(1, 10): - response = self.client.post('/type', data={"name": "Type-{}".format(i), "machines": []}) - self.assert200(response) - self.assertEqualWithout(type_(i), response.json, without=["$id"]) - - response = self.client.get('/type/{}'.format(response.json["$id"])) - self.assert200(response) - self.assertEqualWithout(type_(i), response.json, without=["$id"]) - - response = self.client.get('/type') - self.assert200(response) - for i in range(1, i): - self.assertEqualWithout(type_(i), response.json[i-1], without=["$id"]) - - response = self.client.get('/type/{}'.format(i + 1)) - self.assert404(response) - self.assertJSONEqual({'item': {'$type': 'type', "$id": str(i + 1)}, - 'message': 'Not Found', - 'status': 404}, response.json) - - @unittest.SkipTest - def test_pagination(self): - pass # TODO - - def test_update(self): - response = self.client.post('/type', data={"name": "T1"}) - type1_id = response.json["$id"] - self.assert200(response) - - response = self.client.post('/type', data={"name": "T2"}) - type2_id = response.json["$id"] - self.assert200(response) - - response = self.client.post('/machine', data={"name": "Robot", "type": type1_id}) - machine_id = response.json["$id"] - self.assert200(response) - self.assertEqualWithout( - {'$type': 'machine', 'type': {"$ref": "/type/{}".format(type1_id)}, "wattage": None, "name": "Robot"}, - response.json, - without=["$id"]) - - response = self.client.patch('/machine/{}'.format(machine_id), data={}) - self.assert200(response) - - response = self.client.patch('/machine/{}'.format(machine_id), data={"wattage": 10000}) - self.assert200(response) - self.assertEqualWithout( - {'$type': 'machine', 'type': {"$ref": "/type/{}".format(type1_id)}, "wattage": 10000, "name": "Robot"}, - response.json, - without=["$id"]) - - response = self.client.patch('/machine/{}'.format(machine_id), data={"type": {"$ref": "/type/{}".format(type2_id)}}) - self.assert200(response) - self.assertEqualWithout( - {'$type': 'machine', 'type': {"$ref": "/type/{}".format(type2_id)}, "wattage": 10000, "name": "Robot"}, - response.json, - without=["$id"]) - - response = self.client.patch('/machine/{}'.format(machine_id), data={"type": None}) - self.assert400(response) - self.assertJSONEqual({ - 'errors': [ - { - "message": "None is not valid under any of the given schemas", - "path": [ - "type" - ], - "validationOf": { - "anyOf": [ - { - "additionalProperties": False, - "properties": { - "$ref": { - "pattern": "^\\/type\\/[^/]+$", - "type": "string" - } - }, - "type": "object" - }, - { - "type": "string" - } - ] - } - } - ], - 'message': 'Bad Request', - 'status': 400 - }, response.json) - - response = self.client.patch('/machine/{}'.format(machine_id), data={"name": "Foo"}) - self.assert200(response) - self.assertEqualWithout( - {'$type': 'machine', 'type': {"$ref": "/type/{}".format(type2_id)}, "wattage": 10000.0, "name": "Foo"}, - response.json, - without=["$id"]) - - def test_delete(self): - self.client.post('/type', data={"name": "A"}) - response = self.client.delete('/type/1') - self.assert404(response) - - response = self.client.post('/type', data={"name": "Foo", "machines": []}) - self.assert200(response) - - response = self.client.delete('/type/%s' % response.json["$id"]) - self.assertStatus(response, 204) - - response = self.client.delete('/type/1') - self.assert404(response) - - -class MongoEngineRelationTestCase(MongoEngineTestCase): - def setUp(self): - super(MongoEngineRelationTestCase, self).setUp() - - class User(self.me.Document): - name = StringField(max_length=60) - children = ListField(ReferenceField('User')) - - @property - def memberships(self): - return Group.objects(members__in=self) - - class Group(self.me.Document): - name = StringField(max_length=60) - members = ListField(ReferenceField('User')) - - class UserResource(ModelResource): - class Meta: - model = User - include_id = True - include_type = True - - children = Relation('self') - - class GroupResource(ModelResource): - class Meta: - model = Group - include_id = True - include_type = True - - members = Relation('user') - - self.api.add_resource(UserResource) - self.api.add_resource(GroupResource) - - def test_relationship_secondary(self): - response = self.client.post('/group', data={"name": "Foo"}) - group_id = response.json["$id"] - self.assert200(response) - self.assertEqualWithout({'$type': 'group', "name": "Foo"}, response.json, without=["$id"]) - - response = self.client.post('/user', data={"name": "Bar"}) - user_id = response.json["$id"] - self.assert200(response) - self.assertEqualWithout({'$type': 'user', "name": "Bar"}, response.json, without=["$id"]) - - response = self.client.get('/group/{}/members'.format(group_id)) - self.assert200(response) - self.assertJSONEqual([], response.json) - - response = self.client.post('/group/{}/members'.format(group_id), data={"$ref": "/user/{}".format(user_id)}) - self.assert200(response) - self.assertJSONEqual({"$ref": "/user/{}".format(user_id)}, response.json) - - response = self.client.get('/group/{}/members'.format(group_id)) - self.assert200(response) - self.assertJSONEqual([{"$ref": "/user/{}".format(user_id)}], response.json) - - def test_relationship_secondary_delete_missing(self): - response = self.client.post('/group', data={"name": "Foo"}) - user_id = response.json["$id"] - - response = self.client.post('/user', data={"name": "Bar"}) - group_id = response.json["$id"] - - response = self.client.delete('/group/{}/members/{}'.format(user_id, group_id)) - self.assertStatus(response, 204) - - def test_relationship_post(self): - response = self.client.post('/user', data={"name": "Foo"}) - user_id = response.json["$id"] - self.assert200(response) - self.assertEqualWithout({'$type': 'user', "name": "Foo"}, response.json, without=["$id"]) - - response = self.client.post('/user', data={"name": "Bar"}) - children_id = response.json["$id"] - self.assert200(response) - self.assertEqualWithout({'$type': 'user', "name": "Bar"}, response.json, without=["$id"]) - - response = self.client.post('/user/{}/children'.format(user_id), data={"$ref": "/user/{}".format(children_id)}) - - self.assert200(response) - self.assertJSONEqual({"$ref": "/user/{}".format(children_id)}, response.json) - return user_id, children_id - - def test_relationship_get(self): - user_id, children_id = self.test_relationship_post() - - response = self.client.get('/user/{}/children'.format(user_id)) - self.assert200(response) - self.assertJSONEqual([{"$ref": "/user/{}".format(children_id)}], response.json) - - def test_relationship_delete(self): - user_id, children_id = self.test_relationship_post() - - response = self.client.delete('/user/{}/children/{}'.format(user_id, children_id)) - self.assertStatus(response, 204) - - response = self.client.get('/user/{}/children'.format(user_id)) - self.assert200(response) - self.assertJSONEqual([], response.json) - - def test_relationship_pagination(self): - response = self.client.post('/user', data={"name": "Foo"}) - user_id = response.json["$id"] - self.assert200(response) - - children_ids = [] - for i in range(2, 50): - response = self.client.post('/user', data={"name": str(i)}) - self.assert200(response) - children_ids.append(response.json["$id"]) - response = self.client.post('/user/{}/children'.format(user_id), data={"$ref": "/user/{}".format(children_ids[i-2])}) - self.assert200(response) - - response = self.client.get('/user/{}/children'.format(user_id)) - self.assert200(response) - self.assertJSONEqual([{"$ref": "/user/{}".format(children_ids[i-2])} for i in range(2, 22)], response.json) - - response = self.client.get('/user/{}/children?page=3'.format(user_id)) - self.assert200(response) - self.assertJSONEqual([{"$ref": "/user/{}".format(children_ids[i-2])} for i in range(42, 50)], response.json) - self.assertEqual('48', response.headers['X-Total-Count']) - self.assertEqual('; rel="self",' - '; rel="first",' - '; rel="prev",' - '; rel="last"'.format(user_id, user_id, user_id, user_id), response.headers['Link']) diff --git a/tests/contrib/mongoengine/test_signals.py b/tests/contrib/mongoengine/test_signals.py deleted file mode 100644 index 08f5df6..0000000 --- a/tests/contrib/mongoengine/test_signals.py +++ /dev/null @@ -1,163 +0,0 @@ -from functools import partial - -from flask_mongoengine import MongoEngine -from mongoengine import StringField, ReferenceField, ListField -from blinker._utilities import contextmanager -from blinker import ANY - -from flask_potion.contrib.mongoengine import MongoEngineManager -from flask_potion import signals -from flask_potion.routes import Relation -from flask_potion.resource import ModelResource -from flask_potion import Api -from tests import BaseTestCase -from tests.contrib.mongoengine import MongoEngineTestCase - - -class MongoEngineSignalTestCase(MongoEngineTestCase): - def setUp(self): - super(MongoEngineSignalTestCase, self).setUp() - - class User(self.me.Document): - name = StringField(max_length=60, null=False) - gender = StringField(max_length=1, null=True) - - children = ListField(ReferenceField("User")) - - def __eq__(self, other): - return self.name == other.name - - def __repr__(self): - return 'User({})'.format(self.name) - - class Group(self.me.Document): - name = StringField(max_length=60, null=False) - - class GroupMembership(self.me.Document): - user = ReferenceField(User) - group = ReferenceField(Group) - - class UserResource(ModelResource): - class Meta: - model = User - - children = Relation('self') - - class GroupResource(ModelResource): - class Meta: - model = Group - - members = Relation(UserResource) - - self.User = User - self.Group = Group - self.UserResource = UserResource - self.GroupResource = GroupResource - self.api.add_resource(UserResource) - self.api.add_resource(GroupResource) - - @contextmanager - def assertSignals(self, expected_events, sender=ANY): - events = [] - - def receiver_(signal, sender, **kwargs): - events.append((signal, sender, kwargs)) - - receivers = { - signal: partial(receiver_, signal) for signal in [ - signals.before_create, - signals.after_create, - signals.before_update, - signals.after_update, - signals.before_delete, - signals.after_delete, - signals.before_add_to_relation, - signals.after_add_to_relation, - signals.before_remove_from_relation, - signals.after_remove_from_relation - ] - } - - for signal, receiver in receivers.items(): - signal.connect(receiver, sender=sender, weak=False) - - try: - yield None - except: - for signal, receiver in receivers.items(): - signal.disconnect(receiver) - raise - else: - for signal, receiver in receivers.items(): - signal.disconnect(receiver) - - self.assertEqual(events, expected_events) - - def test_create_signal(self): - with self.assertSignals([ - (signals.before_create, self.UserResource, {'item': self.User(name="Foo")}), - (signals.after_create, self.UserResource, {'item': self.User(name="Foo")}) - ]): - response = self.client.post('/user', data={"name": "Foo"}) - self.assert200(response) - self.assertJSONEqual({'$uri': response.json["$uri"], "name": "Foo", "gender": None}, response.json) - - def test_update_signal(self): - response = self.client.post('/user', data={"name": "Foo"}) - self.assert200(response) - - with self.assertSignals([ - (signals.before_update, self.UserResource, {'changes': {'gender': 'M', 'name': 'Bar'}, - 'item': self.User(name="Bar")}), - (signals.after_update, self.UserResource, {'changes': {'gender': 'M', 'name': 'Bar'}, - 'item': self.User(name="Bar")}) - ]): - uri = response.json["$uri"] - response = self.client.patch(uri, data={"name": "Bar", "gender": "M"}) - self.assert200(response) - self.assertJSONEqual({'$uri': uri, "name": "Bar", "gender": "M"}, response.json) - - def test_delete_signal(self): - response = self.client.post('/user', data={"name": "Foo"}) - self.assert200(response) - - with self.assertSignals([ - (signals.before_delete, self.UserResource, {'item': self.User(name="Foo")}), - (signals.after_delete, self.UserResource, {'item': self.User(name="Foo")}) - ]): - response = self.client.delete(response.json["$uri"]) - self.assertStatus(response, 204) - - def test_relation_signal(self): - - response = self.client.post('/user', data={"name": "Foo"}) - user1_uri = response.json["$uri"] - self.assert200(response) - - with self.assertSignals([ - (signals.before_create, self.UserResource, {'item': self.User(name="Bar")}), - (signals.after_create, self.UserResource, {'item': self.User(name="Bar")}), - (signals.before_add_to_relation, self.UserResource, {'item': self.User(name="Foo"), - 'attribute': 'children', - 'child': self.User(name="Bar")}), - (signals.after_add_to_relation, self.UserResource, {'item': self.User(name="Foo"), - 'attribute': 'children', - 'child': self.User(name="Bar")}), - (signals.before_remove_from_relation, self.UserResource, {'item': self.User(name="Foo"), - 'attribute': 'children', - 'child': self.User(name="Bar")}), - (signals.after_remove_from_relation, self.UserResource, {'item': self.User(name="Foo"), - 'attribute': 'children', - 'child': self.User(name="Bar")}) - ]): - response = self.client.post('/user', data={"name": "Bar"}) - user2_uri = response.json["$uri"] - user2_id = user2_uri.split("/")[-1] - self.assert200(response) - - response = self.client.post('{}/children'.format(user1_uri), data={"$ref": user2_uri}) - - self.assert200(response) - - response = self.client.delete('{}/children/{}'.format(user1_uri, user2_id)) - self.assertStatus(response, 204) \ No newline at end of file diff --git a/tests/contrib/peewee/__init__.py b/tests/contrib/peewee/__init__.py deleted file mode 100644 index 8ce50aa..0000000 --- a/tests/contrib/peewee/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -from playhouse.flask_utils import FlaskDB - -__author__ = 'lays' - - -class PeeweeTestDB(FlaskDB): - def connect_db(self): - pass - - def close_db(self, exc): - pass \ No newline at end of file diff --git a/tests/contrib/peewee/test_manager_peewee.py b/tests/contrib/peewee/test_manager_peewee.py deleted file mode 100644 index e44637b..0000000 --- a/tests/contrib/peewee/test_manager_peewee.py +++ /dev/null @@ -1,437 +0,0 @@ -import unittest - -import peewee as pw -from playhouse.fields import ManyToManyField - -from flask_potion import Api, fields -from flask_potion.contrib.peewee import PeeweeManager -from flask_potion.resource import ModelResource -from flask_potion.routes import Relation -from tests import BaseTestCase -from tests.contrib.peewee import PeeweeTestDB - - -class PeeweeTestCase(BaseTestCase): - def setUp(self): - super(PeeweeTestCase, self).setUp() - self.app.config['DATABASE'] = 'sqlite://' - self.db = db = PeeweeTestDB(self.app) - self.api = Api(self.app) - - class Type(db.Model): - name = pw.CharField(max_length=60, null=False, unique=True) - - class Machine(db.Model): - name = pw.CharField(max_length=60, null=False) - wattage = pw.FloatField(null=True) - type = pw.ForeignKeyField(Type, related_name='machines') - - self.db.database.connect() - self.db.database.create_tables([Type, Machine]) - - class MachineResource(ModelResource): - class Meta: - model = Machine - include_id = True - include_type = True - manager = PeeweeManager - - class Schema: - type = fields.ToOne('type') - - class TypeResource(ModelResource): - class Meta: - model = Type - include_id = True - include_type = True - manager = PeeweeManager - - class Schema: - machines = fields.ToMany(MachineResource) - - self.MachineResource = MachineResource - self.TypeResource = TypeResource - - self.api.add_resource(MachineResource) - self.api.add_resource(TypeResource) - - def tearDown(self): - self.db.database.close() - - def test_field_discovery(self): - self.assertEqual( - set(self.MachineResource.schema.fields.keys()), - {'$id', '$type', 'name', 'type', 'wattage'}) - self.assertEqual( - set(self.TypeResource.schema.fields.keys()), - {'$id', '$type', 'name', 'machines'}) - self.assertEqual(self.MachineResource.meta.name, 'machine') - self.assertEqual(self.TypeResource.meta.name, 'type') - - def test_create_no_json(self): - response = self.client.post('/machine', data='invalid') - self.assert400(response) - - def test_create_json_string(self): - response = self.client.post( - '/machine', data='invalid', force_json=True) - self.assert400(response) - - def test_conflict(self): - response = self.client.post('/type', data={'name': 'foo'}) - self.assert200(response) - - response = self.client.post('/type', data={'name': 'foo'}) - self.assertStatus(response, 409) - - def test_create(self): - response = self.client.post('/type', data={}) - self.assert400(response) - self.assertEqual({ - 'errors': [ - { - 'message': "'name' is a required property", - 'path': [], - 'validationOf': { - 'required': [ - 'name' - ] - } - } - ], - 'message': 'Bad Request', - 'status': 400 - }, response.json) - - response = self.client.post('/type', data={'name': 'x-ray'}) - self.assertJSONEqual({ - '$id': 1, - '$type': 'type', - 'machines': [], - 'name': 'x-ray'}, - response.json) - - response = self.client.post( - '/machine', data={'name': 'Irradiator I', 'type': 1}) - self.assert200(response) - self.assertJSONEqual({ - '$id': 1, - '$type': 'machine', - 'type': {'$ref': '/type/1'}, - 'wattage': None, - 'name': 'Irradiator I'}, - response.json) - - response = self.client.post( - '/machine', data={'name': 'Sol IV', 'type': 1, 'wattage': 1.23e45}) - self.assert200(response) - self.assertJSONEqual({ - '$id': 2, - '$type': 'machine', - 'type': {'$ref': '/type/1'}, - 'wattage': 1.23e45, - 'name': 'Sol IV'}, - response.json) - - response = self.client.get('/type/1') - self.assert200(response) - self.assertJSONEqual({ - '$id': 1, - '$type': 'type', - 'machines': [ - {'$ref': '/machine/1'}, - {'$ref': '/machine/2'}], - 'name': 'x-ray'}, - response.json) - - def test_get(self): - def type_(i): - return { - '$id': i, - '$type': 'type', - 'name': 'Type-{}'.format(i), - 'machines': []} - - for i in range(1, 10): - response = self.client.post( - '/type', - data={'name': 'Type-{}'.format(i), 'machines': []}) - self.assert200(response) - self.assertJSONEqual(type_(i), response.json) - - response = self.client.get('/type/{}'.format(i)) - self.assert200(response) - self.assertJSONEqual(type_(i), response.json) - - response = self.client.get('/type') - self.assert200(response) - self.assertJSONEqual( - [type_(i) for i in range(1, i + 1)], - response.json) - - response = self.client.get('/type/{}'.format(i + 1)) - self.assert404(response) - self.assertJSONEqual({ - 'item': {'$id': i + 1, '$type': 'type'}, - 'message': 'Not Found', - 'status': 404}, - response.json) - - def test_pagination(self): - for i in range(1, 51): - response = self.client.post('/type', data={'name': 'T{}'.format(i)}) - self.assert200(response) - - response = self.client.get('/type') - self.assert200(response) - self.assertEqual('50', response.headers.get('X-Total-Count')) - - response = self.client.get('/type?where={"name": {"$in": ["T1", "T5", "T6"]}}') - self.assert200(response) - self.assertEqual('3', response.headers.get('X-Total-Count')) - - def test_update(self): - response = self.client.post('/type', data={'name': 'T1'}) - self.assert200(response) - - response = self.client.post('/type', data={'name': 'T2'}) - self.assert200(response) - - response = self.client.post( - '/machine', data={'name': 'Robot', 'type': 1}) - self.assert200(response) - self.assertJSONEqual({ - '$id': 1, - '$type': 'machine', - 'type': {'$ref': '/type/1'}, - 'wattage': None, - 'name': 'Robot'}, - response.json) - - response = self.client.patch('/machine/1', data={}) - self.assert200(response) - - response = self.client.patch('/machine/1', data={'wattage': 10000}) - self.assert200(response) - self.assertJSONEqual({ - '$id': 1, - '$type': 'machine', - 'type': {'$ref': '/type/1'}, - 'wattage': 10000, - 'name': 'Robot'}, - response.json) - - response = self.client.patch( - '/machine/1', data={'type': {'$ref': '/type/2'}}) - self.assert200(response) - self.assertJSONEqual({ - '$id': 1, - '$type': 'machine', - 'type': {'$ref': '/type/2'}, - 'wattage': 10000, - 'name': 'Robot'}, - response.json) - - response = self.client.patch('/machine/1', data={'type': None}) - self.assert400(response) - self.assertJSONEqual({ - 'errors': [{ - 'message': 'None is not valid under any of the given schemas', - 'path': [ - 'type'], - 'validationOf': { - 'anyOf': [{ - 'additionalProperties': False, - 'properties': { - '$ref': { - 'pattern': '^\\/type\\/[^/]+$', - 'type': 'string'}}, - 'type': 'object'}, { - 'type': 'integer'}]}}], - 'message': 'Bad Request', - 'status': 400}, - response.json) - - response = self.client.patch('/machine/1', data={'name': 'Foo'}) - self.assert200(response) - self.assertJSONEqual({ - '$id': 1, - '$type': 'machine', - 'type': {'$ref': '/type/2'}, - 'wattage': 10000, - 'name': 'Foo'}, - response.json) - - def test_delete(self): - response = self.client.delete('/type/1') - self.assert404(response) - - response = self.client.post( - '/type', data={'name': 'Foo', 'machines': []}) - self.assert200(response) - - response = self.client.delete('/type/1') - self.assertStatus(response, 204) - - response = self.client.delete('/type/1') - self.assert404(response) - - -class PeeweeRelationTestCase(BaseTestCase): - def setUp(self): - super(PeeweeRelationTestCase, self).setUp() - - self.app.config['DATABASE'] = 'sqlite://' - self.db = db = PeeweeTestDB(self.app) - self.api = Api(self.app) - - class User(db.Model): - parent = pw.ForeignKeyField('self', related_name='children', - null=True) - name = pw.CharField(max_length=60, null=False) - - class Group(db.Model): - name = pw.CharField(max_length=60, null=False) - members = ManyToManyField(User, related_name='memberships') - - db.database.connect() - db.database.create_tables([ - User, - Group, - Group.members.get_through_model()]) - - self.User = User - self.Group = Group - - class UserResource(ModelResource): - class Meta: - model = User - include_id = True - include_type = True - manager = PeeweeManager - - children = Relation('self') - - class GroupResource(ModelResource): - class Meta: - model = Group - include_id = True - include_type = True - manager = PeeweeManager - - members = Relation('user') - - self.api.add_resource(UserResource) - self.api.add_resource(GroupResource) - - def tearDown(self): - self.db.database.drop_tables([ - self.Group.members.get_through_model(), - self.Group, - self.User]) - - if not self.db.database.is_closed(): - self.db.database.close() - - def test_relationship_secondary(self): - response = self.client.post('/group', data={'name': 'Foo'}) - self.assert200(response) - self.assertJSONEqual({ - '$id': 1, - '$type': 'group', - 'name': 'Foo'}, - response.json) - - response = self.client.post('/user', data={'name': 'Bar'}) - self.assert200(response) - self.assertJSONEqual({ - '$id': 1, - '$type': 'user', - 'name': 'Bar'}, - response.json) - - response = self.client.get('/group/1/members') - self.assert200(response) - self.assertJSONEqual([], response.json) - - response = self.client.post( - '/group/1/members', data={'$ref': '/user/1'}) - self.assert200(response) - self.assertJSONEqual({'$ref': '/user/1'}, response.json) - - response = self.client.get('/group/1/members') - self.assert200(response) - self.assertJSONEqual([{'$ref': '/user/1'}], response.json) - - def test_relationship_secondary_delete_missing(self): - response = self.client.post('/group', data={"name": "Foo"}) - response = self.client.post('/user', data={"name": "Bar"}) - - response = self.client.delete('/group/1/members/1') - self.assertStatus(response, 204) - - def test_relationship_post(self): - response = self.client.post('/user', data={'name': 'Foo'}) - self.assert200(response) - self.assertJSONEqual({ - '$id': 1, - '$type': 'user', - 'name': 'Foo'}, - response.json) - - response = self.client.post('/user', data={'name': 'Bar'}) - self.assert200(response) - self.assertJSONEqual({ - '$id': 2, - '$type': 'user', - 'name': 'Bar'}, - response.json) - - response = self.client.post( - '/user/1/children', data={'$ref': '/user/2'}) - self.assert200(response) - self.assertJSONEqual({'$ref': '/user/2'}, response.json) - - def test_relationship_get(self): - self.test_relationship_post() - - response = self.client.get('/user/1/children') - self.assert200(response) - self.assertJSONEqual([{'$ref': '/user/2'}], response.json) - - def test_relationship_delete(self): - self.test_relationship_post() - - response = self.client.delete('/user/1/children/2') - self.assertStatus(response, 204) - - response = self.client.get('/user/1/children') - self.assert200(response) - self.assertJSONEqual([], response.json) - - def test_relationship_pagination(self): - response = self.client.post('/user', data={'name': 'Foo'}) - self.assert200(response) - - for i in range(2, 50): - response = self.client.post('/user', data={'name': str(i)}) - self.assert200(response) - response = self.client.post( - '/user/1/children', - data={'$ref': '/user/{}'.format(response.json['$id'])}) - self.assert200(response) - - response = self.client.get('/user/1/children') - - self.assert200(response) - self.assertEqual('48', response.headers.get('X-Total-Count')) - self.assertJSONEqual( - [{'$ref': '/user/{}'.format(i)} for i in range(2, 22)], - response.json) - - response = self.client.get('/user/1/children?page=3') - self.assert200(response) - self.assertEqual('48', response.headers.get('X-Total-Count')) - self.assertJSONEqual( - [{'$ref': '/user/{}'.format(i)} for i in range(42, 50)], - response.json) diff --git a/tests/contrib/peewee/test_peewee_filter.py b/tests/contrib/peewee/test_peewee_filter.py deleted file mode 100644 index b963d2d..0000000 --- a/tests/contrib/peewee/test_peewee_filter.py +++ /dev/null @@ -1,241 +0,0 @@ -import unittest -from peewee import CharField, IntegerField, BooleanField -from flask_potion.contrib.peewee import PeeweeManager -from flask_potion import ModelResource, fields, Api -from tests import BaseTestCase -from tests.contrib.peewee import PeeweeTestDB - - -class PeeeeFilterTestCase(BaseTestCase): - def setUp(self): - super(PeeeeFilterTestCase, self).setUp() - app = self.app - app.config['DATABASE'] = 'sqlite://' - - self.db = db = PeeweeTestDB(self.app) - self.api = Api(self.app, default_manager=PeeweeManager) - app.debug = True - - class User(db.Model): - id = IntegerField(primary_key=True) - first_name = CharField(max_length=60, null=False) - last_name = CharField(max_length=60, null=False) - - gender = CharField(max_length=1, null=True) - - age = IntegerField() - - is_staff = BooleanField(default=None) - - db.database.connect() - db.database.create_tables([User]) - - class UserResource(ModelResource): - class Schema: - gender = fields.String(enum=['f', 'm'], nullable=True) - - class Meta: - model = User - - class AllowUserResource(ModelResource): - class Meta: - model = User - name = 'allow-user' - filters = { - 'first_name': ['eq'], - 'is_staff': True - } - - self.api.add_resource(UserResource) - self.api.add_resource(AllowUserResource) - - def post_sample_set_a(self): - for user in [ - {'first_name': 'John', 'last_name': 'Doe', 'age': 32, 'is_staff': True, 'gender': 'm'}, - {'first_name': 'Jonnie', 'last_name': 'Doe', 'age': 25, 'is_staff': False, 'gender': 'm'}, - {'first_name': 'Jane', 'last_name': 'Roe', 'age': 18, 'is_staff': False, 'gender': 'f'}, - {'first_name': 'Joe', 'last_name': 'Bloggs', 'age': 21, 'is_staff': True, 'gender': 'm'}, - {'first_name': 'Sue', 'last_name': 'Watts', 'age': 25, 'is_staff': True} - ]: - response = self.client.post('/user', data=user) - self.assert200(response) - - def test_equality(self): - self.post_sample_set_a() - - response = self.client.get('/user?where={"last_name": "Doe"}') - - self.assertEqualWithout([ - {'first_name': 'John', 'last_name': 'Doe'}, - {'first_name': 'Jonnie', 'last_name': 'Doe'}, - ], response.json, without=['$uri', '$uri', '$id', '$type', 'gender', 'age', 'is_staff']) - - response = self.client.get('/user?where={"age": 25}') - - self.assertEqualWithout([ - {'first_name': 'Jonnie', 'last_name': 'Doe', 'age': 25}, - {'first_name': 'Sue', 'last_name': 'Watts', 'age': 25}, - ], response.json, without=['$uri', '$id', '$type', 'gender', 'is_staff']) - - response = self.client.get('/user?where={"last_name": "Doe", "age": 25}') - - self.assertEqualWithout([ - {'first_name': 'Jonnie', 'last_name': 'Doe'}, - ], response.json, without=['$uri', '$id', '$type', 'gender', 'age', 'is_staff']) - - def test_inequality(self): - self.post_sample_set_a() - - response = self.client.get('/user?where={"last_name": {"$ne": "Watts"}, "age": {"$ne": 32}}') - - self.assertEqualWithout([ - {'first_name': 'Jonnie', 'last_name': 'Doe'}, - {'first_name': 'Jane', 'last_name': 'Roe'}, - {'first_name': 'Joe', 'last_name': 'Bloggs'}, - ], response.json, without=['$uri', '$id', '$type', 'gender', 'age', 'is_staff']) - - response = self.client.get('/user?where={"age": {"$gt": 25}}') - - self.assertEqualWithout([ - {'first_name': 'John', 'last_name': 'Doe'} - ], response.json, without=['$uri', '$id', '$type', 'gender', 'age', 'is_staff']) - - response = self.client.get('/user?where={"age": {"$gte": 25}}') - - self.assertEqualWithout([ - {'first_name': 'John', 'last_name': 'Doe'}, - {'first_name': 'Jonnie', 'last_name': 'Doe'}, - {'first_name': 'Sue', 'last_name': 'Watts'}, - ], response.json, without=['$uri', '$id', '$type', 'gender', 'age', 'is_staff']) - - response = self.client.get('/user?where={"age": {"$lte": 21}}') - - self.assertEqualWithout([ - {'first_name': 'Jane', 'last_name': 'Roe'}, - {'first_name': 'Joe', 'last_name': 'Bloggs'} - ], response.json, without=['$uri', '$id', '$type', 'gender', 'age', 'is_staff']) - - response = self.client.get('/user?where={"age": {"$lt": null}}') - self.assert400(response) - - response = self.client.get('/user?where={"first_name": {"$gt": "Jo"}}') - self.assert400(response) - - def test_in(self): - self.post_sample_set_a() - - response = self.client.get('/user?where={"last_name": {"$in": ["Bloggs", "Watts"]}}') - - self.assertEqualWithout([ - {'first_name': 'Joe', 'last_name': 'Bloggs'}, - {'first_name': 'Sue', 'last_name': 'Watts'} - ], response.json, without=['$uri', '$id', '$type', 'gender', 'age', 'is_staff']) - - response = self.client.get('/user?where={"last_name": {"$in": []}}') - - self.assertEqualWithout([], response.json, without=['$uri', '$id', '$type', 'gender', 'age', 'is_staff']) - - def test_startswith(self): - self.post_sample_set_a() - - response = self.client.get('/user?where={"first_name": {"$startswith": "Jo"}}') - - self.assertEqualWithout([ - {'first_name': 'John', 'last_name': 'Doe'}, - {'first_name': 'Jonnie', 'last_name': 'Doe'}, - {'first_name': 'Joe', 'last_name': 'Bloggs'} - ], response.json, without=['$uri', '$id', '$type', 'gender', 'age', 'is_staff']) - - response = self.client.get('/user?where={"first_name": {"$startswith": "J%e"}}') - - self.assertEqualWithout([], response.json, without=['$uri', '$id', '$type', 'gender', 'age', 'is_staff']) - - def test_istartswith(self): - self.post_sample_set_a() - - response = self.client.get('/user?where={"first_name": {"$istartswith": "jo"}}') - - self.assertEqualWithout([ - {'first_name': 'John', 'last_name': 'Doe'}, - {'first_name': 'Jonnie', 'last_name': 'Doe'}, - {'first_name': 'Joe', 'last_name': 'Bloggs'} - ], response.json, without=['$uri', '$id', '$type', 'gender', 'age', 'is_staff']) - - response = self.client.get('/user?where={"first_name": {"$istartswith": "j%e"}}') - - self.assertEqualWithout([], response.json, without=['$uri', '$id', '$type', 'gender', 'age', 'is_staff']) - - def test_iendswith(self): - self.post_sample_set_a() - - response = self.client.get('/user?where={"last_name": {"$iendswith": "Oe"}}') - - self.assertEqualWithout([ - {'first_name': 'John', 'last_name': 'Doe'}, - {'first_name': 'Jonnie', 'last_name': 'Doe'}, - {'first_name': 'Jane', 'last_name': 'Roe'} - ], response.json, without=['$uri', '$id', '$type', 'gender', 'age', 'is_staff']) - - response = self.client.get('/user?where={"first_name": {"$istartswith": "j%e"}}') - - self.assertEqualWithout([], response.json, without=['$uri', '$id', '$type', 'gender', 'age', 'is_staff']) - - @unittest.SkipTest - def test_text_search(self): - self.post_sample_set_a() - - response = self.client.get('/user?search=sbc+dedf&rank=1') - - def test_sort(self): - self.post_sample_set_a() - - response = self.client.get('/user?sort={"last_name": true, "first_name": false}') - - self.assert200(response) - self.assertEqualWithout([ - {'first_name': 'Sue', 'last_name': 'Watts'}, - {'first_name': 'Jane', 'last_name': 'Roe'}, - {'first_name': 'John', 'last_name': 'Doe'}, - {'first_name': 'Jonnie', 'last_name': 'Doe'}, - {'first_name': 'Joe', 'last_name': 'Bloggs'} - ], response.json, - without=['$uri', '$id', '$type', 'gender', 'age', 'is_staff']) - - response = self.client.get('/user?sort={"age": false, "first_name": false}') - - self.assertEqualWithout([ - {'first_name': 'Jane', 'last_name': 'Roe'}, - {'first_name': 'Joe', 'last_name': 'Bloggs'}, - {'first_name': 'Jonnie', 'last_name': 'Doe'}, - {'first_name': 'Sue', 'last_name': 'Watts'}, - {'first_name': 'John', 'last_name': 'Doe'}, - ], response.json, - without=['$uri', '$id', '$type', 'gender', 'age', 'is_staff']) - - - def test_sort_and_where(self): - self.post_sample_set_a() - - response = self.client.get('/user?where={"first_name": {"$startswith": "Jo"}}&sort={"first_name": false}') - - self.assertEqualWithout([ - {'first_name': 'Joe', 'last_name': 'Bloggs'}, - {'first_name': 'John', 'last_name': 'Doe'}, - {'first_name': 'Jonnie', 'last_name': 'Doe'} - ], response.json, without=['$uri', '$id', '$type', 'gender', 'age', 'is_staff']) - - @unittest.SkipTest - def test_sort_pages(self): - pass - - @unittest.SkipTest - def test_disallowed_where_filters(self): - pass - - @unittest.SkipTest - def test_schema(self): - pass - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/contrib/peewee/test_peewee_signals.py b/tests/contrib/peewee/test_peewee_signals.py deleted file mode 100644 index c276711..0000000 --- a/tests/contrib/peewee/test_peewee_signals.py +++ /dev/null @@ -1,167 +0,0 @@ -from functools import partial -from peewee import CharField, ForeignKeyField -from flask_potion.contrib.peewee import PeeweeManager -from flask_potion import signals -from flask_potion.routes import Relation -from flask_potion.resource import ModelResource -from flask_potion import Api -from tests import BaseTestCase -from blinker._utilities import contextmanager -from blinker import ANY -from tests.contrib.peewee import PeeweeTestDB - - -class PeeweeSignalTestCase(BaseTestCase): - def setUp(self): - super(PeeweeSignalTestCase, self).setUp() - app = self.app - app.config['DATABASE'] = 'sqlite://' - - self.db = db = PeeweeTestDB(self.app) - self.api = Api(self.app, default_manager=PeeweeManager) - - class User(db.Model): - name = CharField(max_length=60) - gender = CharField(max_length=1, null=True) - - parent = ForeignKeyField('self', null=True, related_name='children') - - def __eq__(self, other): - return self.name == other.name - - def __repr__(self): - return 'User({})'.format(self.name) - - class Group(db.Model): - name = CharField(max_length=60) - - class GroupMembership(db.Model): - user = ForeignKeyField(User) - group = ForeignKeyField(Group) - - db.database.connect() - db.database.create_tables([User, Group, GroupMembership]) - - - class UserResource(ModelResource): - class Meta: - model = User - - children = Relation('self') - - class GroupResource(ModelResource): - class Meta: - model = Group - - members = Relation(UserResource) - - self.User = User - self.Group = Group - self.UserResource = UserResource - self.GroupResource = GroupResource - self.api.add_resource(UserResource) - self.api.add_resource(GroupResource) - - @contextmanager - def assertSignals(self, expected_events, sender=ANY): - events = [] - - def receiver_(signal, sender, **kwargs): - events.append((signal, sender, kwargs)) - - receivers = { - signal: partial(receiver_, signal) for signal in [ - signals.before_create, - signals.after_create, - signals.before_update, - signals.after_update, - signals.before_delete, - signals.after_delete, - signals.before_add_to_relation, - signals.after_add_to_relation, - signals.before_remove_from_relation, - signals.after_remove_from_relation - ] - } - - for signal, receiver in receivers.items(): - signal.connect(receiver, sender=sender, weak=False) - - try: - yield None - except: - for signal, receiver in receivers.items(): - signal.disconnect(receiver) - raise - else: - for signal, receiver in receivers.items(): - signal.disconnect(receiver) - - self.assertEqual(events, expected_events) - - - def test_create_signal(self): - - with self.assertSignals([ - (signals.before_create, self.UserResource, {'item': self.User(name="Foo")}), - (signals.after_create, self.UserResource, {'item': self.User(name="Foo")}) - ]): - response = self.client.post('/user', data={"name": "Foo"}) - self.assert200(response) - self.assertJSONEqual({'$uri': '/user/1', "name": "Foo", "gender": None}, response.json) - - - def test_update_signal(self): - response = self.client.post('/user', data={"name": "Foo"}) - self.assert200(response) - - with self.assertSignals([ - (signals.before_update, self.UserResource, {'changes': {'gender': 'M', 'name': 'Bar'}, - 'item': self.User(name="Bar")}), - (signals.after_update, self.UserResource, {'changes': {'gender': 'M', 'name': 'Bar'}, - 'item': self.User(name="Bar")}) - ]): - response = self.client.patch('/user/1', data={"name": "Bar", "gender": "M"}) - self.assert200(response) - self.assertJSONEqual({'$uri': '/user/1', "name": "Bar", "gender": "M"}, response.json) - - def test_delete_signal(self): - response = self.client.post('/user', data={"name": "Foo"}) - self.assert200(response) - - with self.assertSignals([ - (signals.before_delete, self.UserResource, {'item': self.User(name="Foo")}), - (signals.after_delete, self.UserResource, {'item': self.User(name="Foo")}) - ]): - response = self.client.delete('/user/1') - self.assertStatus(response, 204) - - def test_relation_signal(self): - - response = self.client.post('/user', data={"name": "Foo"}) - self.assert200(response) - - with self.assertSignals([ - (signals.before_create, self.UserResource, {'item': self.User(name="Bar")}), - (signals.after_create, self.UserResource, {'item': self.User(name="Bar")}), - (signals.before_add_to_relation, self.UserResource, {'item': self.User(name="Foo"), - 'attribute': 'children', - 'child': self.User(name="Bar")}), - (signals.after_add_to_relation, self.UserResource, {'item': self.User(name="Foo"), - 'attribute': 'children', - 'child': self.User(name="Bar")}), - (signals.before_remove_from_relation, self.UserResource, {'item': self.User(name="Foo"), - 'attribute': 'children', - 'child': self.User(name="Bar")}), - (signals.after_remove_from_relation, self.UserResource, {'item': self.User(name="Foo"), - 'attribute': 'children', - 'child': self.User(name="Bar")}) - ]): - response = self.client.post('/user', data={"name": "Bar"}) - self.assert200(response) - - response = self.client.post('/user/1/children', data={"$ref": "/user/2"}) - self.assert200(response) - - response = self.client.delete('/user/1/children/2') - self.assertStatus(response, 204) \ No newline at end of file diff --git a/tests/contrib/principals/__init__.py b/tests/contrib/principals/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/contrib/principals/test_manager_principals.py b/tests/contrib/principals/test_manager_principals.py deleted file mode 100644 index 5022d48..0000000 --- a/tests/contrib/principals/test_manager_principals.py +++ /dev/null @@ -1,501 +0,0 @@ -from functools import wraps -import unittest -from flask import current_app, request -from flask_principal import Identity, identity_changed, identity_loaded, RoleNeed, UserNeed, Principal, ItemNeed -from flask_sqlalchemy import SQLAlchemy -from sqlalchemy.orm import backref -from werkzeug.exceptions import Unauthorized - -from flask_potion.contrib.memory import MemoryManager -from flask_potion.contrib.alchemy import SQLAlchemyManager -from flask_potion.routes import Relation -from flask_potion import Api, fields -from flask_potion.contrib.principals import principals -from flask_potion.resource import ModelResource -from tests import ApiClient, BaseTestCase - - -class AuthorizedApiClient(ApiClient): - def open(self, *args, **kw): - """ - Sends HTTP Authorization header with the ``HTTP_AUTHORIZATION`` config value - unless :param:`authorize` is ``False``. - """ - auth = kw.pop('auth', True) - headers = kw.pop('headers', []) - - if auth: - headers.append(('Authorization', 'Basic OnBhc3N3b3Jk')) - return super(AuthorizedApiClient, self).open(*args, headers=headers, **kw) - - -class PrincipalResource(ModelResource): - class Meta: - manager = principals(SQLAlchemyManager) - - -class PrincipalTestCase(BaseTestCase): - def create_app(self): - app = super(PrincipalTestCase, self).create_app() - app.test_client_class = AuthorizedApiClient - - self.principal = Principal(app) - self.sa = sa = SQLAlchemy(app) - - class User(sa.Model): - id = sa.Column(sa.Integer, primary_key=True) - name = sa.Column(sa.String()) - - class BookStore(sa.Model): - id = sa.Column(sa.Integer, primary_key=True) - name = sa.Column(sa.String()) - - owner_id = sa.Column(sa.Integer, sa.ForeignKey(User.id)) - owner = sa.relationship(User, backref=backref('stores', lazy='dynamic')) - - class Book(sa.Model): - id = sa.Column(sa.Integer, primary_key=True) - title = sa.Column(sa.String(), nullable=False) - - author_id = sa.Column(sa.Integer, sa.ForeignKey(User.id)) - author = sa.relationship(User, backref=backref('books', lazy='dynamic')) - - class BookSigning(sa.Model): - id = sa.Column(sa.Integer, primary_key=True) - book_id = sa.Column(sa.Integer, sa.ForeignKey(Book.id), nullable=False) - store_id = sa.Column(sa.Integer, sa.ForeignKey(BookStore.id), nullable=False) - - book = sa.relationship(Book) - store = sa.relationship(BookStore) - - sa.create_all() - - for model in (BookStore, User, Book, BookSigning): - setattr(self, model.__tablename__.upper(), model) - - return app - - def setUp(self): - super(PrincipalTestCase, self).setUp() - self.mock_user = None - - @identity_loaded.connect_via(self.app) - def on_identity_loaded(sender, identity): - identity.provides.add(UserNeed(identity.id)) - - for role in self.mock_user.get('roles', []): - identity.provides.add(RoleNeed(role)) - - for need in self.mock_user.get('needs', []): - identity.provides.add(need) - - def authenticate(fn): - @wraps(fn) - def wrapper(*args, **kwargs): - auth = request.authorization - - if not auth: - raise Unauthorized() - - if auth.password == 'password': - identity_changed.send(current_app._get_current_object(), identity=Identity(self.mock_user['id'])) - else: - raise Unauthorized() - return fn(*args, **kwargs) - - return wrapper - - self.api = Api(self.app, decorators=[authenticate]) - - def test_role(self): - with self.assertRaises(RuntimeError): - manager = principals(MemoryManager) - - def test_role(self): - class BookResource(PrincipalResource): - class Meta: - model = self.BOOK - permissions = { - 'create': 'author' - } - - self.api.add_resource(BookResource) - - response = self.client.post('/book', data={'title': 'Foo'}, auth=False) - self.assert401(response) - - self.mock_user = {'id': 1} - response = self.client.post('/book', data={'title': 'Foo'}) - self.assert403(response) - - self.mock_user = {'id': 1, 'roles': ['author']} - response = self.client.post('/book', data={'title': 'Foo'}) - self.assert200(response) - self.assertEqual({'title': 'Foo', '$uri': '/book/1'}, response.json) - - self.assert200(self.client.patch('/book/1', data={'title': 'Foo I'})) - - self.mock_user = {'id': 1} - self.assert403(self.client.patch('/book/1', data={'title': 'Bar'})) - - self.assert403(self.client.delete('/book/1')) - - # self.user = {'id': 1, 'roles': ['author']} - # self.assert200(self.client.delete('/book/1')) - - # response = self.client.post('/book', data={'title': 'Foo'}) - # - # self.assert200(response) - - def test_access_forbidden_to_resource_collection(self): - class BookResource(PrincipalResource): - class Meta: - model = self.BOOK - permissions = { - 'read': 'author', - 'create': 'author', - } - - self.api.add_resource(BookResource) - self.mock_user = {'id': 1, 'roles': ['author']} - response = self.client.post('/book', data={'title': 'Foo'}) - self.assert200(response) - self.assertEqual({'title': 'Foo', '$uri': '/book/1'}, response.json) - self.assert200(self.client.get('/book')) - self.mock_user = {'id': 1} - self.assert403(self.client.get('/book')) - - def test_inherit_role_to_one_field(self): - - class BookStoreResource(PrincipalResource): - class Meta: - model = self.BOOK_STORE - permissions = { - 'create': 'admin', - 'update': ['admin'] - } - - class BookSigningResource(PrincipalResource): - class Schema: - book = fields.ToOne('book') - store = fields.ToOne('book_store') - - class Meta: - model = self.BOOK_SIGNING - permissions = { - 'create': 'update:store' - } - - class BookResource(PrincipalResource): - class Meta: - model = self.BOOK - permissions = { - 'create': 'yes' - } - - self.api.add_resource(BookStoreResource) - self.api.add_resource(BookSigningResource) - self.api.add_resource(BookResource) - - self.mock_user = {'id': 1, 'roles': ['admin']} - self.assert200(self.client.post('/book_store', data={ - 'name': 'Foo Books' - })) - - self.assert200(self.client.post('/book', data={ - 'title': 'Bar' - })) - - self.mock_user = {'id': 2} - - response = self.client.post('/book_signing', data={ - 'book': {'$ref': '/book/1'}, - 'store': {'$ref': '/book_store/1'} - }) - self.assert403(response) - - self.mock_user = {'id': 1, 'roles': ['admin']} - self.assert200(self.client.post('/book_signing', data={ - 'book': {'$ref': '/book/1'}, - 'store': {'$ref': '/book_store/1'} - })) - - def test_user_need(self): - - class BookStoreResource(PrincipalResource): - class Schema: - owner = fields.ToOne('user') - - class Meta: - model = self.BOOK_STORE - permissions = { - 'create': 'admin', - 'update': ['admin', 'user:owner'] - } - - class UserResource(PrincipalResource): - class Meta: - model = self.USER - permissions = { - 'create': 'admin', - 'update': 'user:$uri' - } - - self.api.add_resource(BookStoreResource) - self.api.add_resource(UserResource) - - self.mock_user = {'id': 1, 'roles': ['admin']} - - self.assert200(self.client.post('/user', data={'name': 'Admin'})) - self.assert200(self.client.patch('/user/1', data={'name': 'Other'})) - - for i, store in enumerate([ - { - 'name': 'Books & More', - 'owner': { - 'name': 'Mr. Moore' - } - }, - { - 'name': 'Foo Books', - 'owner': { - 'name': 'Foo' - } - } - ]): - response = self.client.post('/user', data=store['owner']) - - owner = {'$ref': response.json['$uri']} - response = self.client.post('/book_store', data={ - 'name': store['name'], - 'owner': owner - }) - - self.assertEqual({'$uri': '/book_store/{}'.format(i + 1), 'name': store['name'], 'owner': owner}, - response.json) - - response = self.client.patch('/book_store/1', data={'name': 'books & moore'}) - self.assert200(response) - - self.mock_user = {'id': 3} - response = self.client.patch('/book_store/1', data={'name': 'Books & Foore'}) - self.assert403(response) - - self.mock_user = {'id': 2} - response = self.client.patch('/book_store/1', data={'name': 'Books & Moore'}) - - self.assert200(response) - - self.assertEqual({ - '$uri': '/book_store/1', - 'name': 'Books & Moore', - 'owner': {'$ref': '/user/2'} - }, response.json) - - response = self.client.patch('/book_store/2', data={'name': 'Moore Books'}) - self.assert403(response) - - def test_item_need_update(self): - - class BookStoreResource(PrincipalResource): - class Meta: - model = self.BOOK_STORE - permissions = { - 'create': 'admin', - 'update': 'update' - } - - self.api.add_resource(BookStoreResource) - - self.mock_user = {'id': 1, 'roles': ['admin']} - - # response = self.client.post('/book_store', data=[ - # {'name': 'Bar Books'}, - # {'name': 'Foomazon'} - # ]) - response = self.client.post('/book_store', data={'name': 'Bar Books'}) - self.assert200(response) - response = self.client.post('/book_store', data={'name': 'Foomazon'}) - self.assert200(response) - - self.mock_user = {'id': 1, 'needs': [ItemNeed('update', 2, 'book_store')]} - - self.assert403(self.client.patch('/book_store/1', data={'name': 'Foo'})) - - response = self.client.patch('/book_store/2', data={'name': 'Foo'}) - self.assert200(response) - self.assertEqual({'$uri': '/book_store/2', 'name': 'Foo'}, response.json) - - # TODO DELETE - - def test_yes_no(self): - class BookResource(PrincipalResource): - class Meta: - model = self.BOOK - permissions = { - 'read': 'yes', - 'create': 'admin', - 'update': 'no' - } - - self.api.add_resource(BookResource) - - self.mock_user = {'id': 1, 'roles': ['admin']} - self.assert200(self.client.post('/book', data={'title': 'Foo'})) - self.assert403(self.client.patch('/book/1', data={'title': 'Bar'})) - self.assert200(self.client.get('/book/1')) - self.assert200(self.client.get('/book')) - - def test_item_need_read(self): - - class BookResource(PrincipalResource): - class Meta: - model = self.BOOK - permissions = { - 'read': ['owns-copy', 'admin'], - 'create': 'admin', - 'owns-copy': 'owns-copy' - } - - self.api.add_resource(BookResource) - - self.mock_user = {'id': 1, 'roles': ['admin']} - - # TODO Bulk inserts - # response = self.client.post('/book', data=[ - # {'title': 'GoT Vol. {}'.format(i + 1)} for i in range(20) - # ]) - - for i in range(20): - self.client.post('/book', data={'title': 'GoT Vol. {}'.format(i + 1)}) - - self.assertEqual(20, len(self.client.get('/book').json)) - - self.mock_user = {'id': 2, 'needs': [ItemNeed('owns-copy', i, 'book') for i in (1, 4, 6, 8, 11, 15, 19)]} - self.assertEqual(7, len(self.client.get('/book').json)) - - self.mock_user = {'id': 3, 'needs': [ItemNeed('owns-copy', i, 'book') for i in (2, 7, 19)]} - - self.assertEqual([ - {'$uri': '/book/2', 'title': 'GoT Vol. 2'}, - {'$uri': '/book/7', 'title': 'GoT Vol. 7'}, - {'$uri': '/book/19', 'title': 'GoT Vol. 19'} - ], self.client.get('/book').json) - - self.assert404(self.client.get('/book/15')) - self.assert200(self.client.get('/book/2')) - self.assert200(self.client.get('/book/7')) - self.assert404(self.client.get('/book/1')) - self.assert404(self.client.get('/book/99')) - - self.mock_user = {'id': 4} - self.assertEqual([], self.client.get('/book').json) - - def test_relationship(self): - "should require update permission on parent resource for updating, read permissions on both" - - class BookResource(PrincipalResource): - class Schema: - author = fields.ToOne('user', nullable=True) - - class Meta: - model = self.BOOK - permissions = { - 'read': ['owns-copy', 'update', 'admin'], - 'create': 'writer', - 'update': 'user:author', - 'owns-copy': 'owns-copy' - } - - class UserResource(PrincipalResource): - books = Relation(BookResource) - - class Meta: - model = self.USER - permissions = { - 'create': 'admin' - } - - self.api.add_resource(UserResource) - self.api.add_resource(BookResource) - - self.mock_user = {'id': 1, 'roles': ['admin']} - - for user in [ - {'name': 'Admin'}, - {'name': 'Author 1'}, - {'name': 'Author 2'} - ]: - response = self.client.post('/user', data=user) - self.assert200(response) - - response = self.client.post('/book', data={ - 'title': 'Foo' - }) - self.assert403(response) - - self.mock_user = {'id': 2, 'roles': ['writer']} - response = self.client.post('/book', data={ - 'author': {'$ref': '/user/2'}, - 'title': 'Bar' - }) - - self.assert200(response) - - self.mock_user = {'id': 3, 'roles': ['writer']} - - response = self.client.post('/book', data={'title': 'Spying: Novel'}) - self.assert200(response) - - response = self.client.post('/book', data={'title': 'Spied: The Sequel'}) - self.assert200(response) - - response = self.client.post('/book', data={'title': 'Spy: The Prequel'}) - self.assert200(response) - self.assertJSONEqual({'$uri': '/book/4', 'author': None, 'title': 'Spy: The Prequel'}, response.json) - - self.mock_user = {'id': 1, 'roles': ['admin']} - self.client.post('/user/3/books', data={'$ref': '/book/2'}) - self.client.post('/user/3/books', data={'$ref': '/book/3'}) - self.client.post('/user/3/books', data={'$ref': '/book/4'}) - - self.mock_user = {'id': 3, 'roles': ['writer']} - self.assert200(response) - - response = self.client.get('/user/3/books') - self.assert200(response) - self.assertEqual(3, len(response.json)) # read -> update -> user:author - - self.mock_user = {'id': 4, 'needs': [ItemNeed('owns-copy', 3, 'book')]} - - response = self.client.get('/user/3/books') - self.assertEqual(1, len(response.json)) # read -> owns-copy - - self.assert200(self.client.get('/book/3')) - self.assert404(self.client.get('/book/2')) - - self.mock_user = {'id': 5} - response = self.client.get('/user/3/books') - self.assertEqual(0, len(response.json)) - self.assert404(self.client.get('/book/2')) - - @unittest.SkipTest - def test_item_route(self): - "should require read permission on parent resource plus any additional permissions" - pass - - def test_permission_circular(self): - class BookResource(PrincipalResource): - class Meta: - model = self.BOOK - permissions = { - 'read': 'create', - 'create': 'read', - 'update': 'create', - 'delete': 'update' - } - - self.api.add_resource(BookResource) - - with self.assertRaises(RuntimeError): - BookResource.manager._needs diff --git a/tests/test_api.py b/tests/test_api.py index 4fb6265..7e2bdeb 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -150,7 +150,7 @@ class Meta: }, "$uri": { "type": "string", - 'pattern': '^\\/book\\/[^/]+$', + 'pattern': '^/book\\/[^/]+$', "readOnly": True }, }, response.json['properties']) diff --git a/tests/test_api_blueprint.py b/tests/test_api_blueprint.py index e6ba238..b52655b 100644 --- a/tests/test_api_blueprint.py +++ b/tests/test_api_blueprint.py @@ -54,7 +54,7 @@ def test_api_blueprint_w_prefix(self): self.assert200(response) response = self.client.get("/api/v1/samples/schema") - self.assertEqual('^\\/api\\/v1\\/samples\\/[^/]+$', response.json['properties']['$uri']['pattern']) + self.assertEqual('^/api/v1/samples\\/[^/]+$', response.json['properties']['$uri']['pattern']) def test_api_blueprint_init_app(self): api = Api() @@ -69,7 +69,7 @@ def test_api_blueprint_init_app(self): self.assert200(response) response = self.client.get("/api/v1/samples/schema") - self.assertEqual('^\\/api\\/v1\\/samples\\/[^/]+$', response.json['properties']['$uri']['pattern']) + self.assertEqual('^/api/v1/samples\\/[^/]+$', response.json['properties']['$uri']['pattern']) def test_multiple_blueprints(self): # Create Blueprints @@ -104,10 +104,10 @@ def test_multiple_blueprints(self): self.assertEqual('/api/v2/samples/schema#', response.json['properties']['samples']['$ref']) response = self.client.get("/api/v1/samples/schema") - self.assertEqual('^\\/api\\/v1\\/samples\\/[^/]+$', response.json['properties']['$uri']['pattern']) + self.assertEqual('^/api/v1/samples\\/[^/]+$', response.json['properties']['$uri']['pattern']) response = self.client.get("/api/v2/samples/schema") - self.assertEqual('^\\/api\\/v2\\/samples\\/[^/]+$', response.json['properties']['$uri']['pattern']) + self.assertEqual('^/api/v2/samples\\/[^/]+$', response.json['properties']['$uri']['pattern']) # Test that we have two prefix'd schemas response = self.client.get("/api/v1/schema") diff --git a/tests/test_error_messages.py b/tests/test_error_messages.py index a00107c..63416cd 100644 --- a/tests/test_error_messages.py +++ b/tests/test_error_messages.py @@ -64,8 +64,7 @@ def test_not_found_exception(self): response = self.client.get('/prefix/error/missing') self.assert404(response) self.assertEqual({ - "message": "The requested URL was not found on the server. If you entered " - "the URL manually please check your spelling and try again.", + "message": "The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.", "status": 404 }, response.json) diff --git a/tests/test_fields.py b/tests/test_fields.py index 3f847c8..6e70299 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -3,6 +3,8 @@ from uuid import uuid4 import unittest from datetime import datetime, date, timedelta + +from aniso8601.exceptions import ISOFormatError from werkzeug.exceptions import BadRequest from flask_potion.exceptions import ValidationError from flask_potion import fields @@ -175,7 +177,7 @@ def test_date_time_convert(self): fields.DateTime().format(datetime(2012, 2, 14, 0, 0, 0, 0, timezone.utc))) def test_date_time_string_convert(self): - with self.assertRaises(ValidationError): + with self.assertRaises(ISOFormatError): fields.DateTimeString().convert('01.01.2016') self.assertEqual(datetime(2009, 2, 13, 23, 16, 40, 0, timezone.utc), diff --git a/tests/test_natural_keys.py b/tests/test_natural_keys.py index f696435..2bafda9 100644 --- a/tests/test_natural_keys.py +++ b/tests/test_natural_keys.py @@ -12,7 +12,7 @@ "properties": { "$ref": { "type": "string", - "pattern": "^\/api\/foo\/[^/]+$" + "pattern": "^/api/foo\/[^/]+$" } }, "additionalProperties": False @@ -23,7 +23,7 @@ "properties": { "$ref": { "type": "string", - "pattern": "^\/api\/foo\/[^/]+$" + "pattern": "^/api/foo\/[^/]+$" } }, "additionalProperties": False diff --git a/tests/test_resource_routes.py b/tests/test_resource_routes.py index 098b908..e3619ad 100644 --- a/tests/test_resource_routes.py +++ b/tests/test_resource_routes.py @@ -179,6 +179,7 @@ class Meta: }, response.json) response = self.client.get('/group/1/members') + self.assert200(response) self.assertJSONEqual([], response.json) diff --git a/tests/test_schema.py b/tests/test_schema.py index f32caec..c075832 100644 --- a/tests/test_schema.py +++ b/tests/test_schema.py @@ -108,21 +108,20 @@ def test_fieldset_parse_request(self): with app.test_request_context(): env = request.environ - # Ensure allow empty POST - fs = FieldSet(None) - env['REQUEST_METHOD'] = 'POST' - fs.parse_request(Request(env)) + with app.test_request_context(method="POST", json={}, environ_base={'CONTENT_TYPE': "application/json"}): + # Ensure allow empty POST + fs = FieldSet(None) + fs.parse_request(request) # Ensure failure when there are fields - fs = FieldSet({'field': fields.String()}) - with self.assertRaises(RequestMustBeJSON): - fs.parse_request(Request(env)) + with app.test_request_context(method="POST"): + fs = FieldSet({'field': fields.String()}) + with self.assertRaises(RequestMustBeJSON): + fs.parse_request(request) # Successful POST with data - env['wsgi.input'] = StringIO('{"field": "data"}') - env['CONTENT_TYPE'] = 'application/json' - env['CONTENT_LENGTH'] = '17' - fs.parse_request(Request(env)) + with app.test_request_context(method="POST", json={"field": "data"}, environ_base={'CONTENT_TYPE': "application/json"}): + fs.parse_request(request) def test_fieldset_format(self): self.assertEqual(