Introducing Pillar Framework

Refactor of pillar-server and pillar-web into a single python package. This
simplifies the overall architecture of pillar applications.

Special thanks @sybren and @venomgfx
This commit is contained in:
2016-08-19 09:19:06 +02:00
parent a5e92e1d87
commit 2c5dc34ea2
232 changed files with 79508 additions and 2232 deletions

View File

@@ -1,315 +0,0 @@
# -*- encoding: utf-8 -*-
import json
import copy
import sys
import logging
import datetime
import os
import base64
try:
from urllib.parse import urlencode
except ImportError:
from urllib import urlencode
from bson import ObjectId, tz_util
# Override Eve settings before importing eve.tests.
import common_test_settings
common_test_settings.override_eve()
from eve.tests import TestMinimal
import pymongo.collection
from flask.testing import FlaskClient
import responses
from common_test_data import EXAMPLE_PROJECT, EXAMPLE_FILE
# from six:
PY3 = sys.version_info[0] == 3
if PY3:
string_type = str
text_type = str
else:
string_type = basestring
text_type = unicode
MY_PATH = os.path.dirname(os.path.abspath(__file__))
TEST_EMAIL_USER = 'koro'
TEST_EMAIL_ADDRESS = '%s@testing.blender.org' % TEST_EMAIL_USER
TEST_FULL_NAME = u'врач Сергей'
TEST_SUBCLIENT_TOKEN = 'my-subclient-token-for-pillar'
BLENDER_ID_TEST_USERID = 1896
BLENDER_ID_USER_RESPONSE = {'status': 'success',
'user': {'email': TEST_EMAIL_ADDRESS,
'full_name': TEST_FULL_NAME,
'id': BLENDER_ID_TEST_USERID},
'token_expires': 'Mon, 1 Jan 2018 01:02:03 GMT'}
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)-15s %(levelname)8s %(name)s %(message)s')
class AbstractPillarTest(TestMinimal):
def setUp(self, **kwargs):
eve_settings_file = os.path.join(MY_PATH, 'common_test_settings.py')
pillar_config_file = os.path.join(MY_PATH, 'config_testing.py')
kwargs['settings_file'] = eve_settings_file
os.environ['EVE_SETTINGS'] = eve_settings_file
os.environ['PILLAR_CONFIG'] = pillar_config_file
super(AbstractPillarTest, self).setUp(**kwargs)
from application import app
logging.getLogger('').setLevel(logging.DEBUG)
logging.getLogger('application').setLevel(logging.DEBUG)
logging.getLogger('werkzeug').setLevel(logging.DEBUG)
logging.getLogger('eve').setLevel(logging.DEBUG)
from eve.utils import config
config.DEBUG = True
self.app = app
self.client = app.test_client()
assert isinstance(self.client, FlaskClient)
def tearDown(self):
super(AbstractPillarTest, self).tearDown()
# Not only delete self.app (like the superclass does),
# but also un-import the application.
del sys.modules['application']
remove = [modname for modname in sys.modules
if modname.startswith('application.')]
for modname in remove:
del sys.modules[modname]
def ensure_file_exists(self, file_overrides=None):
self.ensure_project_exists()
with self.app.test_request_context():
files_collection = self.app.data.driver.db['files']
assert isinstance(files_collection, pymongo.collection.Collection)
file = copy.deepcopy(EXAMPLE_FILE)
if file_overrides is not None:
file.update(file_overrides)
if '_id' in file and file['_id'] is None:
del file['_id']
result = files_collection.insert_one(file)
file_id = result.inserted_id
# Re-fetch from the database, so that we're sure we return the same as is stored.
# This is necessary as datetimes are rounded by MongoDB.
from_db = files_collection.find_one(file_id)
return file_id, from_db
def ensure_project_exists(self, project_overrides=None):
with self.app.test_request_context():
projects_collection = self.app.data.driver.db['projects']
assert isinstance(projects_collection, pymongo.collection.Collection)
project = copy.deepcopy(EXAMPLE_PROJECT)
if project_overrides is not None:
project.update(project_overrides)
found = projects_collection.find_one(project['_id'])
if found is None:
result = projects_collection.insert_one(project)
return result.inserted_id, project
return found['_id'], found
def create_user(self, user_id='cafef00dc379cf10c4aaceaf', roles=('subscriber',),
groups=None):
from application.utils.authentication import make_unique_username
with self.app.test_request_context():
users = self.app.data.driver.db['users']
assert isinstance(users, pymongo.collection.Collection)
result = users.insert_one({
'_id': ObjectId(user_id),
'_updated': datetime.datetime(2016, 4, 15, 13, 15, 11, tzinfo=tz_util.utc),
'_created': datetime.datetime(2016, 4, 15, 13, 15, 11, tzinfo=tz_util.utc),
'username': make_unique_username('tester'),
'groups': groups or [],
'roles': list(roles),
'settings': {'email_communications': 1},
'auth': [{'token': '',
'user_id': unicode(BLENDER_ID_TEST_USERID),
'provider': 'blender-id'}],
'full_name': u'คนรักของผัดไทย',
'email': TEST_EMAIL_ADDRESS
})
return result.inserted_id
def create_valid_auth_token(self, user_id, token='token'):
now = datetime.datetime.now(tz_util.utc)
future = now + datetime.timedelta(days=1)
with self.app.test_request_context():
from application.utils import authentication as auth
token_data = auth.store_token(user_id, token, future, None)
return token_data
def badger(self, user_email, roles, action, srv_token=None):
"""Creates a service account, and uses it to grant or revoke a role to the user.
To skip creation of the service account, pass a srv_token.
:returns: the authentication token of the created service account.
:rtype: str
"""
if isinstance(roles, str):
roles = {roles}
# Create a service account if needed.
if srv_token is None:
from application.modules.service import create_service_account
with self.app.test_request_context():
_, srv_token_doc = create_service_account('service@example.com',
{'badger'},
{'badger': list(roles)})
srv_token = srv_token_doc['token']
for role in roles:
resp = self.client.post('/service/badger',
headers={'Authorization': self.make_header(srv_token),
'Content-Type': 'application/json'},
data=json.dumps({'action': action,
'role': role,
'user_email': user_email}))
self.assertEqual(204, resp.status_code, resp.data)
return srv_token
def mock_blenderid_validate_unhappy(self):
"""Sets up Responses to mock unhappy validation flow."""
responses.add(responses.POST,
'%s/u/validate_token' % self.app.config['BLENDER_ID_ENDPOINT'],
json={'status': 'fail'},
status=403)
def mock_blenderid_validate_happy(self):
"""Sets up Responses to mock happy validation flow."""
responses.add(responses.POST,
'%s/u/validate_token' % self.app.config['BLENDER_ID_ENDPOINT'],
json=BLENDER_ID_USER_RESPONSE,
status=200)
def make_header(self, username, subclient_id=''):
"""Returns a Basic HTTP Authentication header value."""
return 'basic ' + base64.b64encode('%s:%s' % (username, subclient_id))
def create_standard_groups(self, additional_groups=()):
"""Creates standard admin/demo/subscriber groups, plus any additional.
:returns: mapping from group name to group ID
"""
from application.modules import service
with self.app.test_request_context():
group_ids = {}
groups_coll = self.app.data.driver.db['groups']
for group_name in ['admin', 'demo', 'subscriber'] + list(additional_groups):
result = groups_coll.insert_one({'name': group_name})
group_ids[group_name] = result.inserted_id
service.fetch_role_to_group_id_map()
return group_ids
@staticmethod
def join_url_params(params):
"""Constructs a query string from a dictionary and appends it to a url.
Usage::
>>> AbstractPillarTest.join_url_params("pillar:5000/shots",
{"page-id": 2, "NodeType": "Shot Group"})
'pillar:5000/shots?page-id=2&NodeType=Shot+Group'
"""
if params is None:
return None
if not isinstance(params, dict):
return params
def convert_to_string(param):
if isinstance(param, dict):
return json.dumps(param, sort_keys=True)
if isinstance(param, text_type):
return param.encode('utf-8')
return param
# Pass as (key, value) pairs, so that the sorted order is maintained.
jsonified_params = [
(key, convert_to_string(params[key]))
for key in sorted(params.keys())]
return urlencode(jsonified_params)
def client_request(self, method, path, qs=None, expected_status=200, auth_token=None, json=None,
data=None, headers=None, files=None, content_type=None):
"""Performs a HTTP request to the server."""
from application.utils import dumps
import json as mod_json
headers = headers or {}
if auth_token is not None:
headers['Authorization'] = self.make_header(auth_token)
if json is not None:
data = dumps(json)
headers['Content-Type'] = 'application/json'
if files:
data = data or {}
content_type = 'multipart/form-data'
data.update(files)
resp = self.client.open(path=path, method=method, data=data, headers=headers,
content_type=content_type,
query_string=self.join_url_params(qs))
self.assertEqual(expected_status, resp.status_code,
'Expected status %i but got %i. Response: %s' % (
expected_status, resp.status_code, resp.data
))
def json():
if resp.mimetype != 'application/json':
raise TypeError('Unable to load JSON from mimetype %r' % resp.mimetype)
return mod_json.loads(resp.data)
resp.json = json
return resp
def get(self, *args, **kwargs):
return self.client_request('GET', *args, **kwargs)
def post(self, *args, **kwargs):
return self.client_request('POST', *args, **kwargs)
def put(self, *args, **kwargs):
return self.client_request('PUT', *args, **kwargs)
def delete(self, *args, **kwargs):
return self.client_request('DELETE', *args, **kwargs)
def patch(self, *args, **kwargs):
return self.client_request('PATCH', *args, **kwargs)

View File

@@ -1,264 +0,0 @@
import datetime
from bson import tz_util, ObjectId
EXAMPLE_ADMIN_GROUP_ID = ObjectId('5596e975ea893b269af85c0e')
EXAMPLE_PROJECT_ID = ObjectId('5672beecc0261b2005ed1a33')
EXAMPLE_FILE = {u'_id': ObjectId('5672e2c1c379cf0007b31995'),
u'_updated': datetime.datetime(2016, 3, 25, 10, 28, 24, tzinfo=tz_util.utc),
u'height': 2048,
u'name': 'c2a5c897769ce1ef0eb10f8fa1c472bcb8e2d5a4.png', u'format': 'png',
u'variations': [
{u'format': 'jpg', u'height': 160, u'width': 160, u'length': 8558,
u'link': 'http://localhost:8002/file-variant-h', u'content_type': 'image/jpeg',
u'md5': '--', u'file_path': 'c2a5c897769ce1ef0eb10f8fa1c472bcb8e2d5a4-b.jpg',
u'size': 'b'},
{u'format': 'jpg', u'height': 2048, u'width': 2048, u'length': 819569,
u'link': 'http://localhost:8002/file-variant-h', u'content_type': 'image/jpeg',
u'md5': '--', u'file_path': 'c2a5c897769ce1ef0eb10f8fa1c472bcb8e2d5a4-h.jpg',
u'size': 'h'},
{u'format': 'jpg', u'height': 64, u'width': 64, u'length': 8195,
u'link': 'http://localhost:8002/file-variant-t', u'content_type': 'image/jpeg',
u'md5': '--', u'file_path': 'c2a5c897769ce1ef0eb10f8fa1c472bcb8e2d5a4-t.jpg',
u'size': 't'},
],
u'filename': 'brick_dutch_soft_bump.png',
u'project': EXAMPLE_PROJECT_ID,
u'width': 2048, u'length': 6227670,
u'user': ObjectId('56264fc4fa3a250344bd10c5'),
u'content_type': 'image/png',
u'_etag': '044ce3aede2e123e261c0d8bd77212f264d4f7b0',
u'_created': datetime.datetime(2015, 12, 17, 16, 28, 49, tzinfo=tz_util.utc),
u'md5': '',
u'file_path': 'c2a5c897769ce1ef0eb10f8fa1c472bcb8e2d5a4.png',
u'backend': 'pillar',
u'link': 'http://localhost:8002/file',
u'link_expires': datetime.datetime(2016, 3, 22, 9, 28, 22, tzinfo=tz_util.utc)}
EXAMPLE_PROJECT = {
u'_created': datetime.datetime(2015, 12, 17, 13, 22, 56, tzinfo=tz_util.utc),
u'_etag': u'cc4643e98d3606f87bbfaaa200bfbae941b642f3',
u'_id': EXAMPLE_PROJECT_ID,
u'_updated': datetime.datetime(2016, 1, 7, 18, 59, 4, tzinfo=tz_util.utc),
u'category': u'assets',
u'description': u'Welcome to this curated collection of Blender Institute textures and image resources. This collection is an on-going project, as with each project we create a number of textures based on our own resources (photographs, scans, etc.) or made completely from scratch. At the moment you can find all the textures from the past Open Projects that were deemed re-usable. \r\n\r\nPeople who have contributed to these textures:\r\n\r\nAndrea Weikert, Andy Goralczyk, Basse Salmela, Ben Dansie, Campbell Barton, Enrico Valenza, Ian Hubert, Kjartan Tysdal, Manu J\xe4rvinen, Massimiliana Pulieso, Matt Ebb, Pablo Vazquez, Rob Tuytel, Roland Hess, Sarah Feldlaufer, S\xf6nke M\xe4ter',
u'is_private': False,
u'name': u'Textures',
u'node_types': [{u'description': u'Group for texture node type',
u'dyn_schema': {u'order': {u'type': u'integer'},
u'status': {u'allowed': [u'published', u'pending'],
u'type': u'string'},
u'url': {u'type': u'string'}},
u'form_schema': {u'order': {}, u'status': {}, u'url': {}},
u'name': u'group_texture',
u'parent': [u'group_texture', u'project'],
u'permissions': {}},
{u'description': u'Generic group node type edited',
u'dyn_schema': {u'notes': {u'maxlength': 256, u'type': u'string'},
u'order': {u'type': u'integer'},
u'status': {u'allowed': [u'published', u'pending'],
u'type': u'string'},
u'url': {u'type': u'string'}},
u'form_schema': {u'notes': {}, u'order': {}, u'status': {}, u'url': {}},
u'name': u'group',
u'parent': [u'group', u'project'],
u'permissions': {}},
{u'description': u'Basic Asset Type',
u'dyn_schema': {
u'attachments': {u'schema': {u'schema': {u'field': {u'type': u'string'},
u'files': {u'schema': {
u'schema': {u'file': {
u'data_relation': {
u'embeddable': True,
u'field': u'_id',
u'resource': u'files'},
u'type': u'objectid'},
u'size': {
u'type': u'string'},
u'slug': {
u'minlength': 1,
u'type': u'string'}},
u'type': u'dict'},
u'type': u'list'}},
u'type': u'dict'},
u'type': u'list'},
u'categories': {u'type': u'string'},
u'content_type': {u'type': u'string'},
u'file': {u'data_relation': {u'embeddable': True,
u'field': u'_id',
u'resource': u'files'},
u'type': u'objectid'},
u'order': {u'type': u'integer'},
u'status': {u'allowed': [u'published',
u'pending',
u'processing'],
u'type': u'string'},
u'tags': {u'schema': {u'type': u'string'}, u'type': u'list'}},
u'form_schema': {u'attachments': {u'visible': False},
u'categories': {},
u'content_type': {u'visible': False},
u'file': {u'visible': False},
u'order': {},
u'status': {},
u'tags': {}},
u'name': u'asset',
u'parent': [u'group'],
u'permissions': {}},
{u'description': u'Entrypoint to a remote or local storage solution',
u'dyn_schema': {u'backend': {u'type': u'string'},
u'subdir': {u'type': u'string'}},
u'form_schema': {u'backend': {}, u'subdir': {}},
u'name': u'storage',
u'parent': [u'group', u'project'],
u'permissions': {u'groups': [{u'group': EXAMPLE_ADMIN_GROUP_ID,
u'methods': [u'GET', u'PUT', u'POST']},
{u'group': ObjectId('5596e975ea893b269af85c0f'),
u'methods': [u'GET']},
{u'group': ObjectId('564733b56dcaf85da2faee8a'),
u'methods': [u'GET']}],
u'users': [],
u'world': []}},
{u'description': u'Comments for asset nodes, pages, etc.',
u'dyn_schema': {u'confidence': {u'type': u'float'},
u'content': {u'minlength': 5, u'type': u'string'},
u'is_reply': {u'type': u'boolean'},
u'rating_negative': {u'type': u'integer'},
u'rating_positive': {u'type': u'integer'},
u'ratings': {u'schema': {
u'schema': {u'is_positive': {u'type': u'boolean'},
u'user': {u'type': u'objectid'},
u'weight': {u'type': u'integer'}},
u'type': u'dict'},
u'type': u'list'},
u'status': {u'allowed': [u'published', u'flagged', u'edited'],
u'type': u'string'}},
u'form_schema': {u'confidence': {},
u'content': {},
u'is_reply': {},
u'rating_negative': {},
u'rating_positive': {},
u'ratings': {},
u'status': {}},
u'name': u'comment',
u'parent': [u'asset', u'comment'],
u'permissions': {}},
{u'description': u'Container for node_type post.',
u'dyn_schema': {u'categories': {u'schema': {u'type': u'string'},
u'type': u'list'},
u'template': {u'type': u'string'}},
u'form_schema': {u'categories': {}, u'template': {}},
u'name': u'blog',
u'parent': [u'project'],
u'permissions': {}},
{u'description': u'A blog post, for any project',
u'dyn_schema': {
u'attachments': {u'schema': {u'schema': {u'field': {u'type': u'string'},
u'files': {u'schema': {
u'schema': {u'file': {
u'data_relation': {
u'embeddable': True,
u'field': u'_id',
u'resource': u'files'},
u'type': u'objectid'},
u'size': {
u'type': u'string'},
u'slug': {
u'minlength': 1,
u'type': u'string'}},
u'type': u'dict'},
u'type': u'list'}},
u'type': u'dict'},
u'type': u'list'},
u'category': {u'type': u'string'},
u'content': {u'maxlength': 90000,
u'minlength': 5,
u'required': True,
u'type': u'string'},
u'status': {u'allowed': [u'published', u'pending'],
u'default': u'pending',
u'type': u'string'},
u'url': {u'type': u'string'}},
u'form_schema': {u'attachments': {u'visible': False},
u'category': {},
u'content': {},
u'status': {},
u'url': {}},
u'name': u'post',
u'parent': [u'blog'],
u'permissions': {}},
{u'description': u'Image Texture',
u'dyn_schema': {u'aspect_ratio': {u'type': u'float'},
u'categories': {u'type': u'string'},
u'files': {u'schema': {u'schema': {
u'file': {u'data_relation': {u'embeddable': True,
u'field': u'_id',
u'resource': u'files'},
u'type': u'objectid'},
u'is_tileable': {u'type': u'boolean'},
u'map_type': {u'allowed': [u'color',
u'specular',
u'bump',
u'normal',
u'translucency',
u'emission',
u'alpha'],
u'type': u'string'}},
u'type': u'dict'},
u'type': u'list'},
u'is_landscape': {u'type': u'boolean'},
u'is_tileable': {u'type': u'boolean'},
u'order': {u'type': u'integer'},
u'resolution': {u'type': u'string'},
u'status': {u'allowed': [u'published',
u'pending',
u'processing'],
u'type': u'string'},
u'tags': {u'schema': {u'type': u'string'}, u'type': u'list'}},
u'form_schema': {u'aspect_ratio': {},
u'categories': {},
u'content_type': {u'visible': False},
u'files': {u'visible': False},
u'is_landscape': {},
u'is_tileable': {},
u'order': {},
u'resolution': {},
u'status': {},
u'tags': {}},
u'name': u'texture',
u'parent': [u'group'],
u'permissions': {}}],
u'nodes_blog': [],
u'nodes_featured': [],
u'nodes_latest': [],
u'organization': ObjectId('55a99fb43004867fb9934f01'),
u'owners': {u'groups': [], u'users': []},
u'permissions': {u'groups': [{u'group': EXAMPLE_ADMIN_GROUP_ID,
u'methods': [u'GET', u'POST', u'PUT', u'DELETE']}],
u'users': [],
u'world': [u'GET']},
u'picture_header': ObjectId('5673f260c379cf0007b31bc4'),
u'picture_square': ObjectId('5673f256c379cf0007b31bc3'),
u'status': u'published',
u'summary': u'Texture collection from all Blender Institute open projects.',
u'url': u'textures',
u'user': ObjectId('552b066b41acdf5dec4436f2')}
EXAMPLE_NODE = {
u'_id': ObjectId('572761099837730efe8e120d'),
u'picture': ObjectId('572761f39837730efe8e1210'),
u'description': u'',
u'node_type': u'asset',
u'user': ObjectId('57164ca1983773118cbaf779'),
u'properties': {
u'status': u'published',
u'content_type': u'image',
u'file': ObjectId('572761129837730efe8e120e')
},
u'_updated': datetime.datetime(2016, 5, 2, 14, 19, 58, 0, tzinfo=tz_util.utc),
u'name': u'Image test',
u'project': EXAMPLE_PROJECT_ID,
u'_created': datetime.datetime(2016, 5, 2, 14, 19, 37, 0, tzinfo=tz_util.utc),
u'_etag': u'6b8589b42c880e3626f43f3e82a5c5b946742687'
}

View File

@@ -1,13 +0,0 @@
from settings import *
from eve.tests.test_settings import MONGO_DBNAME
def override_eve():
from eve.tests import test_settings
from eve import tests
test_settings.MONGO_HOST = MONGO_HOST
test_settings.MONGO_PORT = MONGO_PORT
tests.MONGO_HOST = MONGO_HOST
tests.MONGO_PORT = MONGO_PORT

View File

@@ -1,11 +0,0 @@
"""Flask configuration file for unit testing."""
BLENDER_ID_ENDPOINT = 'http://127.0.0.1:8001' # nonexistant server, no trailing slash!
DEBUG = False
TESTING = True
CDN_STORAGE_USER = 'u41508580125621'
FILESIZE_LIMIT_BYTES_NONSUBS = 20 * 2 ** 10
ROLES_FOR_UNLIMITED_UPLOADS = {u'subscriber', u'demo', u'admin'}

View File

@@ -2,16 +2,15 @@
import copy
import datetime
import responses
import json
import pillar.tests.common_test_data as ctd
import responses
from bson import tz_util, ObjectId
from pillar.tests import AbstractPillarTest, TEST_EMAIL_USER, TEST_EMAIL_ADDRESS
from pillar.tests.common_test_data import EXAMPLE_NODE
from werkzeug.exceptions import Forbidden
from common_test_class import AbstractPillarTest, TEST_EMAIL_USER, TEST_EMAIL_ADDRESS
from common_test_data import EXAMPLE_NODE
import common_test_data as ctd
PUBLIC_USER_FIELDS = {'full_name', 'email'}
# Use the example project with some additional permissions for these tests.
@@ -32,7 +31,7 @@ _asset_nt['permissions']['groups'] = [
class AuthenticationTests(AbstractPillarTest):
def test_make_unique_username(self):
from application.utils import authentication as auth
from pillar.api.utils import authentication as auth
with self.app.test_request_context():
# This user shouldn't exist yet.
@@ -44,7 +43,7 @@ class AuthenticationTests(AbstractPillarTest):
@responses.activate
def test_validate_token__not_logged_in(self):
from application.utils import authentication as auth
from pillar.api.utils import authentication as auth
with self.app.test_request_context():
self.assertFalse(auth.validate_token())
@@ -53,7 +52,7 @@ class AuthenticationTests(AbstractPillarTest):
def test_validate_token__unknown_token(self):
"""Test validating of invalid token, unknown both to us and Blender ID."""
from application.utils import authentication as auth
from pillar.api.utils import authentication as auth
self.mock_blenderid_validate_unhappy()
with self.app.test_request_context(
@@ -64,7 +63,7 @@ class AuthenticationTests(AbstractPillarTest):
def test_validate_token__unknown_but_valid_token(self):
"""Test validating of valid token, unknown to us but known to Blender ID."""
from application.utils import authentication as auth
from pillar.api.utils import authentication as auth
self.mock_blenderid_validate_happy()
with self.app.test_request_context(
@@ -75,7 +74,7 @@ class AuthenticationTests(AbstractPillarTest):
def test_find_token(self):
"""Test finding of various tokens."""
from application.utils import authentication as auth
from pillar.api.utils import authentication as auth
user_id = self.create_user()
@@ -123,8 +122,8 @@ class AuthenticationTests(AbstractPillarTest):
def test_save_own_user(self):
"""Tests that a user can't change their own fields."""
from application.utils import authentication as auth
from application.utils import PillarJSONEncoder, remove_private_keys
from pillar.api.utils import authentication as auth
from pillar.api.utils import PillarJSONEncoder, remove_private_keys
user_id = self.create_user(roles=[u'subscriber'])
@@ -145,25 +144,25 @@ class AuthenticationTests(AbstractPillarTest):
updated_fields['roles'] = ['admin', 'subscriber', 'demo'] # Try to elevate our roles.
# POSTing updated info to a specific user URL is not allowed by Eve.
resp = self.client.post('/users/%s' % user_id,
resp = self.client.post('/api/users/%s' % user_id,
data=json.dumps(updated_fields, cls=PillarJSONEncoder),
headers={'Authorization': self.make_header('nonexpired-main'),
'Content-Type': 'application/json'})
self.assertEqual(405, resp.status_code)
# PUT and PATCH should not be allowed.
resp = self.client.put('/users/%s' % user_id,
resp = self.client.put('/api/users/%s' % user_id,
data=json.dumps(updated_fields, cls=PillarJSONEncoder),
headers={'Authorization': self.make_header('nonexpired-main'),
'Content-Type': 'application/json'})
self.assertEqual(403, resp.status_code)
updated_fields = {'roles': ['admin', 'subscriber', 'demo']}
resp = self.client.patch('/users/%s' % user_id,
resp = self.client.patch('/api/users/%s' % user_id,
data=json.dumps(updated_fields, cls=PillarJSONEncoder),
headers={'Authorization': self.make_header('nonexpired-main'),
'Content-Type': 'application/json'})
self.assertEqual(405, resp.status_code)
self.assertEqual(403, resp.status_code)
# After all of this, the roles should be the same.
with self.app.test_request_context(
@@ -183,7 +182,7 @@ class AuthenticationTests(AbstractPillarTest):
now = datetime.datetime.now(tz_util.utc)
with self.app.test_request_context():
from application.utils import authentication as auth
from pillar.api.utils import authentication as auth
auth.store_token(user_id, 'long-expired',
now - datetime.timedelta(days=365), None)
@@ -193,7 +192,7 @@ class AuthenticationTests(AbstractPillarTest):
now + datetime.timedelta(days=1), None)
# Validation should clean up old tokens.
auth.validate_token()
auth.validate_this_token('je', 'moeder')
token_coll = self.app.data.driver.db['tokens']
self.assertEqual({'short-expired', 'not-expired'},
@@ -216,12 +215,12 @@ class UserListTests(AbstractPillarTest):
def test_list_all_users_anonymous(self):
# Listing all users should be forbidden
resp = self.client.get('/users')
resp = self.client.get('/api/users')
self.assertEqual(403, resp.status_code)
def test_list_all_users_subscriber(self):
# Regular access should result in only your own info.
resp = self.client.get('/users', headers={'Authorization': self.make_header('token')})
resp = self.client.get('/api/users', headers={'Authorization': self.make_header('token')})
users = json.loads(resp.data)
self.assertEqual(200, resp.status_code)
@@ -233,7 +232,7 @@ class UserListTests(AbstractPillarTest):
def test_list_all_users_admin(self):
# Admin access should result in all users
resp = self.client.get('/users', headers={'Authorization': self.make_header('admin-token')})
resp = self.client.get('/api/users', headers={'Authorization': self.make_header('admin-token')})
users = json.loads(resp.data)
self.assertEqual(200, resp.status_code)
@@ -246,7 +245,7 @@ class UserListTests(AbstractPillarTest):
def test_list_all_users_admin_explicit_projection(self):
# Admin access should result in all users
projection = json.dumps({'auth': 1})
resp = self.client.get('/users?projection=%s' % projection,
resp = self.client.get('/api/users?projection=%s' % projection,
headers={'Authorization': self.make_header('admin-token')})
users = json.loads(resp.data)
@@ -258,10 +257,10 @@ class UserListTests(AbstractPillarTest):
self.assertNotIn('auth', user_info)
def test_user_anonymous(self):
from application.utils import remove_private_keys
from pillar.api.utils import remove_private_keys
# Getting a user should be limited to certain fields
resp = self.client.get('/users/123456789abc123456789abc')
resp = self.client.get('/api/users/123456789abc123456789abc')
self.assertEqual(200, resp.status_code)
user_info = json.loads(resp.data)
@@ -270,7 +269,7 @@ class UserListTests(AbstractPillarTest):
def test_own_user_subscriber(self):
# Regular access should result in only your own info.
resp = self.client.get('/users/123456789abc123456789abc',
resp = self.client.get('/api/users/123456789abc123456789abc',
headers={'Authorization': self.make_header('token')})
user_info = json.loads(resp.data)
@@ -280,7 +279,7 @@ class UserListTests(AbstractPillarTest):
def test_own_user_subscriber_explicit_projection(self):
# With a custom projection requesting the auth list
projection = json.dumps({'auth': 1})
resp = self.client.get('/users/%s?projection=%s' % ('123456789abc123456789abc', projection),
resp = self.client.get('/api/users/%s?projection=%s' % ('123456789abc123456789abc', projection),
headers={'Authorization': self.make_header('token')})
user_info = json.loads(resp.data)
@@ -288,10 +287,10 @@ class UserListTests(AbstractPillarTest):
self.assertNotIn('auth', user_info)
def test_other_user_subscriber(self):
from application.utils import remove_private_keys
from pillar.api.utils import remove_private_keys
# Requesting another user should be limited to full name and email.
resp = self.client.get('/users/%s' % '223456789abc123456789abc',
resp = self.client.get('/api/users/%s' % '223456789abc123456789abc',
headers={'Authorization': self.make_header('token')})
user_info = json.loads(resp.data)
@@ -302,15 +301,15 @@ class UserListTests(AbstractPillarTest):
self.assertEqual(PUBLIC_USER_FIELDS, set(regular_info.keys()))
def test_put_user(self):
from application.utils import remove_private_keys
from pillar.api.utils import remove_private_keys
# PUTting a user should work, and not mess up the auth field.
resp = self.client.get('/users/123456789abc123456789abc',
resp = self.client.get('/api/users/123456789abc123456789abc',
headers={'Authorization': self.make_header('token')})
user_info = json.loads(resp.data)
put_user = remove_private_keys(user_info)
resp = self.client.put('/users/123456789abc123456789abc',
resp = self.client.put('/api/users/123456789abc123456789abc',
headers={'Authorization': self.make_header('token'),
'Content-Type': 'application/json',
'If-Match': user_info['_etag']},
@@ -324,15 +323,15 @@ class UserListTests(AbstractPillarTest):
self.assertIn('auth', db_user)
def test_put_other_user(self):
from application.utils import remove_private_keys
from pillar.api.utils import remove_private_keys
# PUTting the user as another user should fail.
resp = self.client.get('/users/123456789abc123456789abc',
resp = self.client.get('/api/users/123456789abc123456789abc',
headers={'Authorization': self.make_header('token')})
user_info = json.loads(resp.data)
put_user = remove_private_keys(user_info)
resp = self.client.put('/users/123456789abc123456789abc',
resp = self.client.put('/api/users/123456789abc123456789abc',
headers={'Authorization': self.make_header('other-token'),
'Content-Type': 'application/json',
'If-Match': user_info['_etag']},
@@ -340,15 +339,15 @@ class UserListTests(AbstractPillarTest):
self.assertEqual(403, resp.status_code, resp.data)
def test_put_admin(self):
from application.utils import remove_private_keys
from pillar.api.utils import remove_private_keys
# PUTting a user should work, and not mess up the auth field.
resp = self.client.get('/users/123456789abc123456789abc',
resp = self.client.get('/api/users/123456789abc123456789abc',
headers={'Authorization': self.make_header('token')})
user_info = json.loads(resp.data)
put_user = remove_private_keys(user_info)
resp = self.client.put('/users/123456789abc123456789abc',
resp = self.client.put('/api/users/123456789abc123456789abc',
headers={'Authorization': self.make_header('admin-token'),
'Content-Type': 'application/json',
'If-Match': user_info['_etag']},
@@ -374,13 +373,13 @@ class UserListTests(AbstractPillarTest):
'email': TEST_EMAIL_ADDRESS,
}
resp = self.client.post('/users',
resp = self.client.post('/api/users',
headers={'Authorization': self.make_header('token'),
'Content-Type': 'application/json'},
data=json.dumps(post_user))
self.assertEqual(405, resp.status_code, resp.data)
resp = self.client.post('/users',
resp = self.client.post('/api/users',
headers={'Authorization': self.make_header('admin-token'),
'Content-Type': 'application/json'},
data=json.dumps(post_user))
@@ -389,11 +388,11 @@ class UserListTests(AbstractPillarTest):
def test_delete(self):
"""DELETING a user should fail for subscribers and admins alike."""
resp = self.client.delete('/users/323456789abc123456789abc',
resp = self.client.delete('/api/users/323456789abc123456789abc',
headers={'Authorization': self.make_header('token')})
self.assertEqual(405, resp.status_code, resp.data)
resp = self.client.delete('/users/323456789abc123456789abc',
resp = self.client.delete('/api/users/323456789abc123456789abc',
headers={'Authorization': self.make_header('admin-token')})
self.assertEqual(405, resp.status_code, resp.data)
@@ -402,7 +401,7 @@ class PermissionComputationTest(AbstractPillarTest):
maxDiff = None
def test_merge_permissions(self):
from application.utils.authorization import merge_permissions
from pillar.api.utils.authorization import merge_permissions
with self.app.test_request_context():
self.assertEqual({}, merge_permissions())
@@ -453,11 +452,11 @@ class PermissionComputationTest(AbstractPillarTest):
def sort(self, permissions):
"""Returns a sorted copy of the permissions."""
from application.utils.authorization import merge_permissions
from pillar.api.utils.authorization import merge_permissions
return merge_permissions(permissions, {})
def test_effective_permissions(self):
from application.utils.authorization import compute_aggr_permissions
from pillar.api.utils.authorization import compute_aggr_permissions
with self.app.test_request_context():
# Test project permissions.
@@ -518,7 +517,7 @@ class PermissionComputationTest(AbstractPillarTest):
class RequireRolesTest(AbstractPillarTest):
def test_no_roles_required(self):
from flask import g
from application.utils.authorization import require_login
from pillar.api.utils.authorization import require_login
called = [False]
@@ -535,7 +534,7 @@ class RequireRolesTest(AbstractPillarTest):
def test_some_roles_required(self):
from flask import g
from application.utils.authorization import require_login
from pillar.api.utils.authorization import require_login
called = [False]
@@ -557,7 +556,7 @@ class RequireRolesTest(AbstractPillarTest):
def test_all_roles_required(self):
from flask import g
from application.utils.authorization import require_login
from pillar.api.utils.authorization import require_login
called = [False]
@@ -591,7 +590,7 @@ class RequireRolesTest(AbstractPillarTest):
self.assertTrue(called[0])
def test_user_has_role(self):
from application.utils.authorization import user_has_role
from pillar.api.utils.authorization import user_has_role
with self.app.test_request_context():
self.assertTrue(user_has_role('subscriber', {'roles': ['aap', 'noot', 'subscriber']}))

View File

@@ -8,10 +8,9 @@ import logging
import responses
from bson import ObjectId
from flask import url_for
from pillar.tests import AbstractPillarTest, TEST_EMAIL_ADDRESS
from werkzeug import exceptions as wz_exceptions
from common_test_class import AbstractPillarTest, TEST_EMAIL_ADDRESS
log = logging.getLogger(__name__)
@@ -32,8 +31,8 @@ class AbstractHomeProjectTest(AbstractPillarTest):
class HomeProjectTest(AbstractHomeProjectTest):
def test_create_home_project(self):
from application.modules.blender_cloud import home_project
from application.utils.authentication import validate_token
from pillar.api.blender_cloud import home_project
from pillar.api.utils.authentication import validate_token
user_id = self._create_user_with_token(roles={u'subscriber'}, token='token')
@@ -66,13 +65,13 @@ class HomeProjectTest(AbstractHomeProjectTest):
def test_autocreate_home_project_with_subscriber_role(self):
# Implicitly create user by token validation.
self.mock_blenderid_validate_happy()
resp = self.client.get('/users/me', headers={'Authorization': self.make_header('token')})
resp = self.client.get('/api/users/me', headers={'Authorization': self.make_header('token')})
self.assertEqual(200, resp.status_code, resp)
# Grant subscriber and homeproject roles, and fetch the home project.
self.badger(TEST_EMAIL_ADDRESS, {'subscriber', 'homeproject'}, 'grant')
resp = self.client.get('/bcloud/home-project',
resp = self.client.get('/api/bcloud/home-project',
headers={'Authorization': self.make_header('token')})
self.assertEqual(200, resp.status_code)
@@ -90,39 +89,17 @@ class HomeProjectTest(AbstractHomeProjectTest):
})
self.assertIsNotNone(node)
# @responses.activate
# def test_home_project_ab_testing(self):
# self.mock_blenderid_validate_happy()
# resp = self.client.get('/users/me', headers={'Authorization': self.make_header('token')})
# self.assertEqual(200, resp.status_code, resp)
#
# # Grant subscriber and but NOT homeproject role, and fetch the home project.
# self.badger(TEST_EMAIL_ADDRESS, {'subscriber'}, 'grant')
#
# resp = self.client.get('/bcloud/home-project',
# headers={'Authorization': self.make_header('token')})
# self.assertEqual(404, resp.status_code)
#
# resp = self.client.get('/users/me',
# headers={'Authorization': self.make_header('token')})
# self.assertEqual(200, resp.status_code)
# me = json.loads(resp.data)
#
# with self.app.test_request_context():
# from application.modules.blender_cloud import home_project
# self.assertFalse(home_project.has_home_project(me['_id']))
@responses.activate
def test_autocreate_home_project_with_demo_role(self):
# Implicitly create user by token validation.
self.mock_blenderid_validate_happy()
resp = self.client.get('/users/me', headers={'Authorization': self.make_header('token')})
resp = self.client.get('/api/users/me', headers={'Authorization': self.make_header('token')})
self.assertEqual(200, resp.status_code, resp)
# Grant demo and homeproject role, which should allow creation of the home project.
self.badger(TEST_EMAIL_ADDRESS, {'demo', 'homeproject'}, 'grant')
resp = self.client.get('/bcloud/home-project',
resp = self.client.get('/api/bcloud/home-project',
headers={'Authorization': self.make_header('token')})
self.assertEqual(200, resp.status_code)
@@ -142,18 +119,18 @@ class HomeProjectTest(AbstractHomeProjectTest):
@responses.activate
def test_autocreate_home_project_with_succubus_role(self):
from application.utils import dumps
from pillar.api.utils import dumps
# Implicitly create user by token validation.
self.mock_blenderid_validate_happy()
resp = self.client.get('/users/me', headers={'Authorization': self.make_header('token')})
resp = self.client.get('/api/users/me', headers={'Authorization': self.make_header('token')})
self.assertEqual(200, resp.status_code, resp.data)
user_id = ObjectId(json.loads(resp.data)['_id'])
# Grant succubus role, which should allow creation of a read-only home project.
self.badger(TEST_EMAIL_ADDRESS, {'succubus', 'homeproject'}, 'grant')
resp = self.client.get('/bcloud/home-project',
resp = self.client.get('/api/bcloud/home-project',
headers={'Authorization': self.make_header('token')})
self.assertEqual(200, resp.status_code)
json_proj = json.loads(resp.data)
@@ -194,7 +171,7 @@ class HomeProjectTest(AbstractHomeProjectTest):
'description': 'Sync folder for Blender 2.77',
'properties': {'status': 'published'},
}
resp = self.client.post('/nodes', data=dumps(sub_sync_node),
resp = self.client.post('/api/nodes', data=dumps(sub_sync_node),
headers={'Authorization': self.make_header('token'),
'Content-Type': 'application/json'}
)
@@ -211,8 +188,8 @@ class HomeProjectTest(AbstractHomeProjectTest):
self.assertEqual(node_perms, expected_node_permissions)
def test_has_home_project(self):
from application.modules.blender_cloud import home_project
from application.utils.authentication import validate_token
from pillar.api.blender_cloud import home_project
from pillar.api.utils.authentication import validate_token
user_id = self._create_user_with_token(roles={u'subscriber'}, token='token')
@@ -225,7 +202,7 @@ class HomeProjectTest(AbstractHomeProjectTest):
self.assertTrue(home_project.has_home_project(user_id))
# Delete the project.
resp = self.client.delete('/projects/%s' % proj['_id'],
resp = self.client.delete('/api/projects/%s' % proj['_id'],
headers={'Authorization': self.make_header('token'),
'If-Match': proj['_etag']})
self.assertEqual(204, resp.status_code, resp.data)
@@ -237,13 +214,13 @@ class HomeProjectTest(AbstractHomeProjectTest):
# Implicitly create user by token validation.
self.mock_blenderid_validate_happy()
resp = self.client.get('/users/me', headers={'Authorization': self.make_header('token')})
resp = self.client.get('/api/users/me', headers={'Authorization': self.make_header('token')})
self.assertEqual(200, resp.status_code, resp)
# Grant subscriber role, and fetch the home project.
self.badger(TEST_EMAIL_ADDRESS, {'subscriber', 'homeproject'}, 'grant')
resp = self.client.get('/bcloud/home-project',
resp = self.client.get('/api/bcloud/home-project',
query_string={'projection': json.dumps(
{'permissions': 1,
'category': 1,
@@ -262,13 +239,13 @@ class HomeProjectTest(AbstractHomeProjectTest):
# Implicitly create user by token validation.
self.mock_blenderid_validate_happy()
resp = self.client.get('/users/me', headers={'Authorization': self.make_header('token')})
resp = self.client.get('/api/users/me', headers={'Authorization': self.make_header('token')})
self.assertEqual(200, resp.status_code, resp)
# Grant subscriber role, and fetch the home project.
self.badger(TEST_EMAIL_ADDRESS, {'subscriber', 'homeproject'}, 'grant')
resp = self.client.get('/bcloud/home-project',
resp = self.client.get('/api/bcloud/home-project',
headers={'Authorization': self.make_header('token')})
self.assertEqual(200, resp.status_code, resp.data)
@@ -277,8 +254,8 @@ class HomeProjectTest(AbstractHomeProjectTest):
@responses.activate
def test_multiple_users_with_home_project(self):
from application.modules.blender_cloud import home_project
from application.utils.authentication import validate_token
from pillar.api.blender_cloud import home_project
from pillar.api.utils.authentication import validate_token
uid1 = self._create_user_with_token(roles={u'subscriber'}, token='token1', user_id=24 * 'a')
uid2 = self._create_user_with_token(roles={u'subscriber'}, token='token2', user_id=24 * 'b')
@@ -295,9 +272,9 @@ class HomeProjectTest(AbstractHomeProjectTest):
db_proj2 = self.app.data.driver.db['projects'].find_one(proj2['_id'])
# Test availability at end-point
resp1 = self.client.get('/bcloud/home-project',
resp1 = self.client.get('/api/bcloud/home-project',
headers={'Authorization': self.make_header('token1')})
resp2 = self.client.get('/bcloud/home-project',
resp2 = self.client.get('/api/bcloud/home-project',
headers={'Authorization': self.make_header('token2')})
self.assertEqual(200, resp1.status_code)
self.assertEqual(200, resp2.status_code)
@@ -318,19 +295,19 @@ class HomeProjectTest(AbstractHomeProjectTest):
self._create_user_with_token(roles={u'subscriber'}, token='token')
# Create home project by getting it.
resp = self.client.get('/bcloud/home-project',
resp = self.client.get('/api/bcloud/home-project',
headers={'Authorization': self.make_header('token')})
self.assertEqual(200, resp.status_code, resp.data)
before_delete_json_proj = json.loads(resp.data)
# Delete the project.
resp = self.client.delete('/projects/%s' % before_delete_json_proj['_id'],
resp = self.client.delete('/api/projects/%s' % before_delete_json_proj['_id'],
headers={'Authorization': self.make_header('token'),
'If-Match': before_delete_json_proj['_etag']})
self.assertEqual(204, resp.status_code, resp.data)
# Recreate home project by getting it.
resp = self.client.get('/bcloud/home-project',
resp = self.client.get('/api/bcloud/home-project',
headers={'Authorization': self.make_header('token')})
self.assertEqual(200, resp.status_code, resp.data)
after_delete_json_proj = json.loads(resp.data)
@@ -345,7 +322,7 @@ class HomeProjectUserChangedRoleTest(AbstractPillarTest):
self.create_standard_groups()
def test_without_home_project(self):
from application.modules.blender_cloud import home_project
from pillar.api.blender_cloud import home_project
self.user_id = self.create_user()
@@ -356,8 +333,8 @@ class HomeProjectUserChangedRoleTest(AbstractPillarTest):
# Shouldn't do anything, shouldn't crash either.
def test_already_subscriber_role(self):
from application.modules.blender_cloud import home_project
from application.utils.authentication import validate_token
from pillar.api.blender_cloud import home_project
from pillar.api.utils.authentication import validate_token
self.user_id = self.create_user(roles=set('subscriber'))
self.create_valid_auth_token(self.user_id, 'token')
@@ -374,8 +351,8 @@ class HomeProjectUserChangedRoleTest(AbstractPillarTest):
self.create_test_node(home_proj['_id'])
def test_granting_subscriber_role(self):
from application.modules.blender_cloud import home_project
from application.utils.authentication import validate_token
from pillar.api.blender_cloud import home_project
from pillar.api.utils.authentication import validate_token
self.user_id = self.create_user(roles=set())
self.create_valid_auth_token(self.user_id, 'token')
@@ -392,8 +369,8 @@ class HomeProjectUserChangedRoleTest(AbstractPillarTest):
self.create_test_node(home_proj['_id'])
def test_revoking_subscriber_role(self):
from application.modules.blender_cloud import home_project
from application.utils.authentication import validate_token
from pillar.api.blender_cloud import home_project
from pillar.api.utils.authentication import validate_token
self.user_id = self.create_user(roles=set('subscriber'))
self.create_valid_auth_token(self.user_id, 'token')
@@ -410,7 +387,7 @@ class HomeProjectUserChangedRoleTest(AbstractPillarTest):
self.create_test_node(home_proj['_id'], 403)
def create_test_node(self, project_id, status_code=201):
from application.utils import dumps
from pillar.api.utils import dumps
node = {
'project': project_id,
@@ -420,7 +397,7 @@ class HomeProjectUserChangedRoleTest(AbstractPillarTest):
'properties': {},
}
resp = self.client.post('/nodes', data=dumps(node),
resp = self.client.post('/api/nodes', data=dumps(node),
headers={'Authorization': self.make_header('token'),
'Content-Type': 'application/json'})
self.assertEqual(status_code, resp.status_code, resp.data)
@@ -465,7 +442,7 @@ class TextureLibraryTest(AbstractHomeProjectTest):
)
def test_blender_cloud_addon_version(self):
from application.modules.blender_cloud import blender_cloud_addon_version
from pillar.api.blender_cloud import blender_cloud_addon_version
# Three-digit version
with self.app.test_request_context(headers={'Blender-Cloud-Addon': '1.3.3'}):
@@ -484,7 +461,7 @@ class TextureLibraryTest(AbstractHomeProjectTest):
self.assertRaises(wz_exceptions.BadRequest, blender_cloud_addon_version)
def test_hdri_library__no_bcloud_version(self):
resp = self.get('/bcloud/texture-libraries', auth_token='token')
resp = self.get('/api/bcloud/texture-libraries', auth_token='token')
libs = resp.json()['_items']
library_project_ids = {proj['_id'] for proj in libs}
@@ -492,7 +469,7 @@ class TextureLibraryTest(AbstractHomeProjectTest):
self.assertIn(unicode(self.tex_proj_id), library_project_ids)
def test_hdri_library__old_bcloud_addon(self):
resp = self.get('/bcloud/texture-libraries',
resp = self.get('/api/bcloud/texture-libraries',
auth_token='token',
headers={'Blender-Cloud-Addon': '1.3.3'})
libs = resp.json()['_items']
@@ -501,7 +478,7 @@ class TextureLibraryTest(AbstractHomeProjectTest):
self.assertIn(unicode(self.tex_proj_id), library_project_ids)
def test_hdri_library__new_bcloud_addon(self):
resp = self.get('/bcloud/texture-libraries',
resp = self.get('/api/bcloud/texture-libraries',
auth_token='token',
headers={'Blender-Cloud-Addon': '1.4.0'})
libs = resp.json()['_items']
@@ -512,9 +489,9 @@ class TextureLibraryTest(AbstractHomeProjectTest):
class HdriSortingTest(AbstractHomeProjectTest):
def setUp(self, **kwargs):
from manage_extra.node_types.hdri import node_type_hdri
from pillar.api.node_types.hdri import node_type_hdri
AbstractHomeProjectTest.setUp(self, **kwargs)
super(HdriSortingTest, self).setUp(**kwargs)
self.user_id = self._create_user_with_token({u'subscriber'}, 'token')
self.hdri_proj_id, proj = self.ensure_project_exists(project_overrides={
@@ -571,11 +548,11 @@ class HdriSortingTest(AbstractHomeProjectTest):
},
'name': 'Symmetrical Garden'
}
resp = self.post('/nodes', json=node, expected_status=201, auth_token='token')
resp = self.post('/api/nodes', json=node, expected_status=201, auth_token='token')
node_info = resp.json()
# Check that the node's files are in the right order
resp = self.get('/nodes/%s' % node_info['_id'], auth_token='token')
resp = self.get('/api/nodes/%s' % node_info['_id'], auth_token='token')
get_node = resp.json()
self.assertEqual(['256p', '1k', '2k'],
@@ -597,7 +574,7 @@ class HdriSortingTest(AbstractHomeProjectTest):
},
'name': 'Symmetrical Garden'
}
resp = self.post('/nodes', json=node, expected_status=201, auth_token='token')
resp = self.post('/api/nodes', json=node, expected_status=201, auth_token='token')
node_info = resp.json()
# Mess up the node's order
@@ -606,11 +583,11 @@ class HdriSortingTest(AbstractHomeProjectTest):
{'resolution': '1k', 'file': self.file_1k},
{'resolution': '256p', 'file': self.file_256p},
]
self.put('/nodes/%s' % node_info['_id'], json=node, auth_token='token',
self.put('/api/nodes/%s' % node_info['_id'], json=node, auth_token='token',
headers={'If-Match': node_info['_etag']})
# Check that the node's files are in the right order
resp = self.get('/nodes/%s' % node_info['_id'], auth_token='token')
resp = self.get('/api/nodes/%s' % node_info['_id'], auth_token='token')
get_node = resp.json()
self.assertEqual(['256p', '1k', '2k'],

View File

@@ -1,13 +1,12 @@
# -*- encoding: utf-8 -*-
import responses
import json
import responses
from bson import ObjectId
from flask import g
from common_test_class import (AbstractPillarTest, TEST_EMAIL_ADDRESS, BLENDER_ID_TEST_USERID,
TEST_SUBCLIENT_TOKEN, TEST_EMAIL_USER, TEST_FULL_NAME)
from pillar.tests import (AbstractPillarTest, TEST_EMAIL_ADDRESS, BLENDER_ID_TEST_USERID,
TEST_SUBCLIENT_TOKEN, TEST_EMAIL_USER, TEST_FULL_NAME)
class BlenderIdSubclientTest(AbstractPillarTest):
@@ -34,7 +33,7 @@ class BlenderIdSubclientTest(AbstractPillarTest):
@responses.activate
def test_store_scst_existing_user(self):
# Make sure the user exists in our database.
from application.utils.authentication import create_new_user
from pillar.api.utils.authentication import create_new_user
with self.app.test_request_context():
create_new_user(TEST_EMAIL_ADDRESS, 'apekoppie', BLENDER_ID_TEST_USERID)
@@ -63,7 +62,7 @@ class BlenderIdSubclientTest(AbstractPillarTest):
db_user = self._common_user_test(201)
# Make a call that's authenticated with the SCST
from application.utils import authentication as auth
from pillar.api.utils import authentication as auth
subclient_id = self.app.config['BLENDER_ID_SUBCLIENT_ID']
auth_header = self.make_header(TEST_SUBCLIENT_TOKEN, subclient_id)
@@ -80,7 +79,7 @@ class BlenderIdSubclientTest(AbstractPillarTest):
self.mock_blenderid_validate_happy()
subclient_id = self.app.config['BLENDER_ID_SUBCLIENT_ID']
resp = self.client.post('/blender_id/store_scst',
resp = self.client.post('/api/blender_id/store_scst',
data={'user_id': BLENDER_ID_TEST_USERID,
'subclient_id': subclient_id,
'token': scst})

View File

@@ -1,19 +1,19 @@
"""Test cases for the zencoder notifications."""
import json
from common_test_class import AbstractPillarTest
from pillar.tests import AbstractPillarTest
class ZencoderNotificationTest(AbstractPillarTest):
def test_missing_secret(self):
with self.app.test_request_context():
resp = self.client.post('/encoding/zencoder/notifications')
resp = self.client.post('/api/encoding/zencoder/notifications')
self.assertEqual(401, resp.status_code)
def test_wrong_secret(self):
with self.app.test_request_context():
resp = self.client.post('/encoding/zencoder/notifications',
resp = self.client.post('/api/encoding/zencoder/notifications',
headers={'X-Zencoder-Notification-Secret': 'koro'})
self.assertEqual(401, resp.status_code)
@@ -26,7 +26,7 @@ class ZencoderNotificationTest(AbstractPillarTest):
with self.app.test_request_context():
secret = self.app.config['ZENCODER_NOTIFICATIONS_SECRET']
resp = self.client.post('/encoding/zencoder/notifications',
resp = self.client.post('/api/encoding/zencoder/notifications',
data=json.dumps({'job': {'id': 'koro-007',
'state': 'done'},
'outputs': [{

View File

@@ -4,20 +4,20 @@ import bson.tz_util
import datetime
from eve import RFC1123_DATE_FORMAT
from common_test_class import AbstractPillarTest
from pillar.tests import AbstractPillarTest
class FileCachingTest(AbstractPillarTest):
def test_nonexistant_file(self):
with self.app.test_request_context():
resp = self.client.get('/files/12345')
resp = self.client.get('/api/files/12345')
self.assertEqual(404, resp.status_code)
def test_existing_file(self):
file_id, _ = self.ensure_file_exists()
resp = self.client.get('/files/%s' % file_id)
resp = self.client.get('/api/files/%s' % file_id)
self.assertEqual(200, resp.status_code)
def test_if_modified_304(self):
@@ -29,7 +29,7 @@ class FileCachingTest(AbstractPillarTest):
})
updated = file_doc['_updated'].strftime(RFC1123_DATE_FORMAT)
resp = self.client.get('/files/%s' % file_id,
resp = self.client.get('/api/files/%s' % file_id,
headers={'if_modified_since': updated})
self.assertEqual(304, resp.status_code)
@@ -40,7 +40,7 @@ class FileCachingTest(AbstractPillarTest):
with self.app.test_request_context():
updated = (file_doc['_updated'] + delta).strftime(RFC1123_DATE_FORMAT)
resp = self.client.get('/files/%s' % file_id,
resp = self.client.get('/api/files/%s' % file_id,
headers={'if_modified_since': updated})
self.assertEqual(200, resp.status_code)
@@ -53,6 +53,6 @@ class FileCachingTest(AbstractPillarTest):
})
updated = file_doc['_updated'].strftime(RFC1123_DATE_FORMAT)
resp = self.client.get('/files/%s' % file_id,
resp = self.client.get('/api/files/%s' % file_id,
headers={'if_modified_since': updated})
self.assertEqual(200, resp.status_code)

View File

@@ -1,14 +1,13 @@
import json
import io
import json
import os
import tempfile
import pillar.tests.common_test_data as ctd
import rsa.randnum
from pillar.tests import AbstractPillarTest, TEST_EMAIL_ADDRESS
from werkzeug.datastructures import FileStorage
from common_test_class import AbstractPillarTest, TEST_EMAIL_ADDRESS
import common_test_data as ctd
class FileStorageTest(AbstractPillarTest):
def fake_file(self, filename, content_type):
@@ -17,7 +16,7 @@ class FileStorageTest(AbstractPillarTest):
content_type=content_type)
def test_override_content_type(self):
from application.modules.file_storage import override_content_type
from pillar.api.file_storage import override_content_type
fake = self.fake_file('compressed.blend', 'jemoeder')
override_content_type(fake)
@@ -125,7 +124,7 @@ class FileAccessTest(AbstractPillarTest):
headers = {'Authorization': self.make_header(token)}
else:
headers = None
resp = self.client.get('/files/%s' % file_id, headers=headers)
resp = self.client.get('/api/files/%s' % file_id, headers=headers)
self.assertEqual(200, resp.status_code)
file_info = json.loads(resp.data)
@@ -189,7 +188,7 @@ class FileMaxSizeTest(AbstractPillarTest):
file_size = 10 * 2 ** 10
test_file = self.create_test_file(file_size)
resp = self.post('/storage/stream/%s' % self.project_id,
resp = self.post('/api/storage/stream/%s' % self.project_id,
expected_status=201,
auth_token='token',
files={'file': (test_file, 'test_file.bin')})
@@ -202,7 +201,7 @@ class FileMaxSizeTest(AbstractPillarTest):
file_size = 30 * 2 ** 10
test_file = self.create_test_file(file_size)
self.post('/storage/stream/%s' % self.project_id,
self.post('/api/storage/stream/%s' % self.project_id,
expected_status=413,
auth_token='token',
files={'file': (test_file, 'test_file.bin')})
@@ -213,7 +212,7 @@ class FileMaxSizeTest(AbstractPillarTest):
file_size = 30 * 2 ** 10
test_file = self.create_test_file(file_size)
resp = self.post('/storage/stream/%s' % self.project_id,
resp = self.post('/api/storage/stream/%s' % self.project_id,
expected_status=201,
auth_token='token',
files={'file': (test_file, 'test_file.bin')})
@@ -224,7 +223,7 @@ class FileMaxSizeTest(AbstractPillarTest):
def assert_file_doc_ok(self, file_id, file_size):
with self.app.test_request_context():
from application.utils import str2id
from pillar.api.utils import str2id
# Check that the file exists in MongoDB
files_coll = self.app.data.driver.db['files']

View File

@@ -2,7 +2,7 @@
import json
from bson import ObjectId, tz_util
from common_test_class import AbstractPillarTest
from pillar.tests import AbstractPillarTest
from datetime import datetime, timedelta
@@ -67,7 +67,7 @@ class LinkRefreshTest(AbstractPillarTest):
refreshed_lower_limit = self.now + timedelta(seconds=0.9 * validity_seconds)
with self.app.test_request_context():
from application.modules import file_storage
from pillar.api import file_storage
# First run: refresh files 0 and 1, don't touch 2-4 (due to chunking).
file_storage.refresh_links_for_project(self.project_id, 2, hour_from_now)
@@ -102,7 +102,7 @@ class LinkRefreshTest(AbstractPillarTest):
validity_seconds = self.app.config['FILE_LINK_VALIDITY']['unittest']
refreshed_lower_limit = self.now + timedelta(seconds=0.9 * validity_seconds)
resp = self.client.get('/files/%s' % self.file_id[0])
resp = self.client.get('/api/files/%s' % self.file_id[0])
self.assertEqual(200, resp.status_code)
# Test the returned document.

View File

@@ -3,12 +3,12 @@ import datetime
from bson import tz_util
from common_test_class import AbstractPillarTest
from pillar.tests import AbstractPillarTest
class LocalAuthTest(AbstractPillarTest):
def create_test_user(self):
from application.modules import local_auth
from pillar.api import local_auth
with self.app.test_request_context():
user_id = local_auth.create_local_user('koro@example.com', 'oti')
return user_id
@@ -24,7 +24,7 @@ class LocalAuthTest(AbstractPillarTest):
def test_login_existing_user(self):
user_id = self.create_test_user()
resp = self.client.post('/auth/make-token',
resp = self.client.post('/api/auth/make-token',
data={'username': 'koro',
'password': 'oti'})
self.assertEqual(200, resp.status_code, resp.data)
@@ -33,14 +33,14 @@ class LocalAuthTest(AbstractPillarTest):
token = token_info['token']
headers = {'Authorization': self.make_header(token)}
resp = self.client.get('/users/%s' % user_id,
resp = self.client.get('/api/users/%s' % user_id,
headers=headers)
self.assertEqual(200, resp.status_code, resp.data)
def test_login_expired_token(self):
user_id = self.create_test_user()
resp = self.client.post('/auth/make-token',
resp = self.client.post('/api/auth/make-token',
data={'username': 'koro',
'password': 'oti'})
self.assertEqual(200, resp.status_code, resp.data)
@@ -58,19 +58,19 @@ class LocalAuthTest(AbstractPillarTest):
# Do something restricted.
headers = {'Authorization': self.make_header(token)}
resp = self.client.put('/users/%s' % user_id,
resp = self.client.put('/api/users/%s' % user_id,
headers=headers)
self.assertEqual(403, resp.status_code, resp.data)
def test_login_nonexistant_user(self):
resp = self.client.post('/auth/make-token',
resp = self.client.post('/api/auth/make-token',
data={'username': 'proog',
'password': 'oti'})
self.assertEqual(403, resp.status_code, resp.data)
def test_login_bad_pwd(self):
resp = self.client.post('/auth/make-token',
resp = self.client.post('/api/auth/make-token',
data={'username': 'koro',
'password': 'koro'})

View File

@@ -1,15 +1,14 @@
import json
from mock import mock
import pillar.tests.common_test_data as ctd
from bson import ObjectId
from eve.methods.post import post_internal
from eve.methods.put import put_internal
from flask import g
from mock import mock
from pillar.tests import AbstractPillarTest
from werkzeug.exceptions import UnprocessableEntity
from common_test_class import AbstractPillarTest
import common_test_data as ctd
class NodeContentTypeTest(AbstractPillarTest):
def mkfile(self, file_id, content_type):
@@ -78,14 +77,14 @@ class NodeContentTypeTest(AbstractPillarTest):
self.create_valid_auth_token(user_id, 'token')
project_id, _ = self.ensure_project_exists()
resp = self.client.get('/projects/%s?node_type=asset' % project_id)
resp = self.client.get('/api/projects/%s?node_type=asset' % project_id)
self.assertEqual(200, resp.status_code)
data = json.loads(resp.data)
self.assertEqual([u'GET'], data['allowed_methods'])
def test_default_picture_image_asset(self):
from application.utils import dumps
from pillar.api.utils import dumps
file_id_image = self.mkfile(24 * 'a', 'image/jpeg')
file_id_video = self.mkfile(24 * 'b', 'video/matroska')
@@ -98,7 +97,7 @@ class NodeContentTypeTest(AbstractPillarTest):
def test_for(node, expected_picture_id):
# Create the node
resp = self.client.post('/nodes',
resp = self.client.post('/api/nodes',
data=dumps(node),
headers={'Authorization': self.make_header('token'),
'Content-Type': 'application/json'})
@@ -106,7 +105,7 @@ class NodeContentTypeTest(AbstractPillarTest):
node_id = json.loads(resp.data)['_id']
# Test that the node has the attached file as picture.
resp = self.client.get('/nodes/%s' % node_id,
resp = self.client.get('/api/nodes/%s' % node_id,
headers={'Authorization': self.make_header('token')})
self.assertEqual(resp.status_code, 200, resp.data)
json_node = json.loads(resp.data)
@@ -221,14 +220,14 @@ class NodeOwnerTest(AbstractPillarTest):
self._test_user(test_node)
def _test_user(self, test_node):
from application.utils import dumps
from pillar.api.utils import dumps
resp = self.client.post('/nodes', data=dumps(test_node),
resp = self.client.post('/api/nodes', data=dumps(test_node),
headers={'Authorization': self.make_header('token'),
'Content-Type': 'application/json'})
self.assertEqual(201, resp.status_code, resp.data)
created = json.loads(resp.data)
resp = self.client.get('/nodes/%s' % created['_id'],
resp = self.client.get('/api/nodes/%s' % created['_id'],
headers={'Authorization': self.make_header('token')})
self.assertEqual(200, resp.status_code, resp.data)
json_node = json.loads(resp.data)
@@ -252,7 +251,7 @@ class NodeSharingTest(AbstractPillarTest):
self.create_valid_auth_token(self.user_id, 'token')
# Create a node to share
resp = self.post('/nodes', expected_status=201, auth_token='token', json={
resp = self.post('/api/nodes', expected_status=201, auth_token='token', json={
'project': self.project_id,
'node_type': 'asset',
'name': str(self),
@@ -268,7 +267,7 @@ class NodeSharingTest(AbstractPillarTest):
def test_share_node(self):
# Share the node
resp = self.post('/nodes/%s/share' % self.node_id, auth_token='token',
resp = self.post('/api/nodes/%s/share' % self.node_id, auth_token='token',
expected_status=201)
share_data = resp.json()
@@ -276,57 +275,57 @@ class NodeSharingTest(AbstractPillarTest):
def test_anonymous_access_shared_node(self):
# Anonymous user should not have access
self.get('/nodes/%s' % self.node_id, expected_status=403)
self.get('/api/nodes/%s' % self.node_id, expected_status=403)
# Share the node
self.post('/nodes/%s/share' % self.node_id, auth_token='token',
self.post('/api/nodes/%s/share' % self.node_id, auth_token='token',
expected_status=201)
# Check that an anonymous user has acces.
resp = self.get('/nodes/%s' % self.node_id)
resp = self.get('/api/nodes/%s' % self.node_id)
self.assertEqual(str(self.node_id), resp.json()['_id'])
def test_other_user_access_shared_node(self):
# Share the node
self.post('/nodes/%s/share' % self.node_id, auth_token='token',
self.post('/api/nodes/%s/share' % self.node_id, auth_token='token',
expected_status=201)
# Check that another user has access
other_user_id = self.create_user(user_id=24 * 'a')
self.create_valid_auth_token(other_user_id, 'other-token')
resp = self.get('/nodes/%s' % self.node_id, auth_token='other-token')
resp = self.get('/api/nodes/%s' % self.node_id, auth_token='other-token')
self.assertEqual(str(self.node_id), resp.json()['_id'])
def test_get_share_data__unshared_node(self):
self.get('/nodes/%s/share' % self.node_id,
self.get('/api/nodes/%s/share' % self.node_id,
expected_status=204,
auth_token='token')
def test_get_share_data__shared_node(self):
# Share the node first.
self.post('/nodes/%s/share' % self.node_id, auth_token='token',
self.post('/api/nodes/%s/share' % self.node_id, auth_token='token',
expected_status=201)
# Then get its share info.
resp = self.get('/nodes/%s/share' % self.node_id, auth_token='token')
resp = self.get('/api/nodes/%s/share' % self.node_id, auth_token='token')
share_data = resp.json()
self._check_share_data(share_data)
def test_unauthenticated(self):
self.post('/nodes/%s/share' % self.node_id,
self.post('/api/nodes/%s/share' % self.node_id,
expected_status=403)
def test_other_user(self):
other_user_id = self.create_user(user_id=24 * 'a')
self.create_valid_auth_token(other_user_id, 'other-token')
self.post('/nodes/%s/share' % self.node_id,
self.post('/api/nodes/%s/share' % self.node_id,
auth_token='other-token',
expected_status=403)
def test_create_short_link(self):
from application.modules.nodes import create_short_code
from pillar.api.nodes import create_short_code
with self.app.test_request_context():
length = self.app.config['SHORT_CODE_LENGTH']
@@ -339,7 +338,7 @@ class NodeSharingTest(AbstractPillarTest):
def test_short_code_collision(self):
# Create a second node that has already been shared.
self.post('/nodes', expected_status=201, auth_token='token', json={
self.post('/api/nodes', expected_status=201, auth_token='token', json={
'project': self.project_id,
'node_type': 'asset',
'name': 'collider',
@@ -349,9 +348,9 @@ class NodeSharingTest(AbstractPillarTest):
# Mock create_short_code so that it returns predictable short codes.
codes = ['takenX', 'takenX', 'freeXX']
with mock.patch('application.modules.nodes.create_short_code',
with mock.patch('pillar.api.nodes.create_short_code',
side_effect=codes) as create_short_link:
resp = self.post('/nodes/%s/share' % self.node_id, auth_token='token',
resp = self.post('/api/nodes/%s/share' % self.node_id, auth_token='token',
expected_status=201)
share_data = resp.json()
@@ -363,19 +362,19 @@ class NodeSharingTest(AbstractPillarTest):
"""Projecting short_code should get us short_link as well."""
# Share the node
resp = self.post('/nodes/%s/share' % self.node_id, auth_token='token',
resp = self.post('/api/nodes/%s/share' % self.node_id, auth_token='token',
expected_status=201)
share_data = resp.json()
# Get the node with short_code
resp = self.get('/nodes/%s' % self.node_id,
resp = self.get('/api/nodes/%s' % self.node_id,
json={'projection': {'short_code': 1}})
node = resp.json()
self.assertEqual(node['short_code'], share_data['short_code'])
self.assertTrue(node['short_link'].endswith(share_data['short_code']))
# Get the node without short_code
resp = self.get('/nodes/%s' % self.node_id,
resp = self.get('/api/nodes/%s' % self.node_id,
qs={'projection': {'short_code': 0}})
node = resp.json()
self.assertNotIn('short_code', node)

View File

@@ -1,19 +1,35 @@
from common_test_class import AbstractPillarTest
import common_test_data as ctd
from pillar.tests import AbstractPillarTest
class PatchCommentTest(AbstractPillarTest):
def setUp(self, **kwargs):
AbstractPillarTest.setUp(self, **kwargs)
self.project_id, proj = self.ensure_project_exists()
admin_group_id = proj['permissions']['groups'][0]['group']
self.user_id = self.create_user(user_id=24 * 'a')
self.owner_id = self.create_user(user_id=24 * 'b')
self.owner_id = self.create_user(user_id=24 * 'b', groups=[admin_group_id])
self.create_valid_auth_token(self.user_id, 'token')
self.create_valid_auth_token(self.owner_id, 'owner-token')
self.project_id, _ = self.ensure_project_exists()
# Create a node to attach the comment to
asset = {'description': '',
'project': self.project_id,
'node_type': 'asset',
'user': self.owner_id,
'properties': {'status': 'published'},
'name': 'Test asset'}
resp = self.post('/api/nodes', json=asset,
auth_token='owner-token',
expected_status=201)
asset_id = resp.json()['_id']
# Create the comment
comment = {'description': '',
'project': self.project_id,
'parent': asset_id,
'node_type': 'comment',
'user': self.owner_id,
'properties': {'rating_positive': 0,
@@ -24,15 +40,11 @@ class PatchCommentTest(AbstractPillarTest):
},
'name': 'Test comment'}
# Use MongoDB to insert the node (comments aren't allowed to be parentless,
# so Eve would bark).
with self.app.test_request_context():
nodes_coll = self.app.data.driver.db['nodes']
result = nodes_coll.insert_one(comment)
self.assertIsNotNone(result.inserted_id)
comment_id = result.inserted_id
self.node_url = '/nodes/%s' % comment_id
resp = self.post('/api/nodes', json=comment,
auth_token='owner-token',
expected_status=201)
comment_info = resp.json()
self.node_url = '/api/nodes/%s' % comment_info['_id']
def test_upvote_other_comment(self):
# Patch the node

View File

@@ -8,8 +8,7 @@ import logging
import urllib
from bson import ObjectId
from common_test_class import AbstractPillarTest
from pillar.tests import AbstractPillarTest
log = logging.getLogger(__name__)
@@ -21,7 +20,7 @@ class AbstractProjectTest(AbstractPillarTest):
return user_id
def _create_project(self, project_name, token):
resp = self.client.post('/p/create',
resp = self.client.post('/api/p/create',
headers={'Authorization': self.make_header(token)},
data={'project_name': project_name})
return resp
@@ -60,7 +59,7 @@ class ProjectCreationTest(AbstractProjectTest):
project_id = project_info['_id']
# Test that the Location header contains the location of the project document.
self.assertEqual('http://localhost/projects/%s' % project_id,
self.assertEqual('http://localhost/api/projects/%s' % project_id,
resp.headers['Location'])
# GET the project from the URL in the Location header to see if that works too.
@@ -76,7 +75,7 @@ class ProjectCreationTest(AbstractProjectTest):
self.assertEqual(1, len(project['permissions']['groups']))
# Check the etag
resp = self.client.get('/projects/%s' % project_id, headers=auth_header)
resp = self.client.get('/api/projects/%s' % project_id, headers=auth_header)
from_db = json.loads(resp.data)
self.assertEqual(from_db['_etag'], project['_etag'])
@@ -126,21 +125,21 @@ class ProjectCreationTest(AbstractProjectTest):
token='token-b')
# Assertion: each user must have access to their own project.
resp = self.client.get('/projects/%s' % proj_a['_id'],
resp = self.client.get('/api/projects/%s' % proj_a['_id'],
headers={'Authorization': self.make_header('token-a')})
self.assertEqual(200, resp.status_code, resp.data)
resp = self.client.get('/projects/%s' % proj_b['_id'],
resp = self.client.get('/api/projects/%s' % proj_b['_id'],
headers={'Authorization': self.make_header('token-b')})
self.assertEqual(200, resp.status_code, resp.data)
# Getting a project list should return projects you have access to.
resp = self.client.get('/projects',
resp = self.client.get('/api/projects',
headers={'Authorization': self.make_header('token-a')})
self.assertEqual(200, resp.status_code)
proj_list = json.loads(resp.data)
self.assertEqual({u'Prøject A'}, {p['name'] for p in proj_list['_items']})
resp = self.client.get('/projects',
resp = self.client.get('/api/projects',
headers={'Authorization': self.make_header('token-b')})
self.assertEqual(200, resp.status_code)
proj_list = json.loads(resp.data)
@@ -148,7 +147,7 @@ class ProjectCreationTest(AbstractProjectTest):
# No access to anything for user C, should result in empty list.
self._create_user_with_token(roles={u'subscriber'}, token='token-c', user_id=12 * 'c')
resp = self.client.get('/projects',
resp = self.client.get('/api/projects',
headers={'Authorization': self.make_header('token-c')})
self.assertEqual(200, resp.status_code)
proj_list = json.loads(resp.data)
@@ -159,11 +158,11 @@ class ProjectEditTest(AbstractProjectTest):
def test_editing_as_subscriber(self):
"""Test that we can set certain fields, but not all."""
from application.utils import remove_private_keys, PillarJSONEncoder
from pillar.api.utils import remove_private_keys, PillarJSONEncoder
dumps = functools.partial(json.dumps, cls=PillarJSONEncoder)
project_info = self._create_user_and_project([u'subscriber'])
project_url = '/projects/%(_id)s' % project_info
project_url = '/api/projects/%(_id)s' % project_info
resp = self.client.get(project_url,
headers={'Authorization': self.make_header('token')})
@@ -174,7 +173,7 @@ class ProjectEditTest(AbstractProjectTest):
self._create_user_with_token(['subscriber'], 'other-token', user_id=other_user_id)
# Unauthenticated should be forbidden
resp = self.client.put('/projects/%s' % project['_id'],
resp = self.client.put('/api/projects/%s' % project['_id'],
data=dumps(remove_private_keys(project)),
headers={'Content-Type': 'application/json'})
self.assertEqual(403, resp.status_code)
@@ -220,11 +219,11 @@ class ProjectEditTest(AbstractProjectTest):
def test_editing_as_admin(self):
"""Test that we can set all fields as admin."""
from application.utils import remove_private_keys, PillarJSONEncoder
from pillar.api.utils import remove_private_keys, PillarJSONEncoder
dumps = functools.partial(json.dumps, cls=PillarJSONEncoder)
project_info = self._create_user_and_project([u'subscriber', u'admin'])
project_url = '/projects/%(_id)s' % project_info
project_url = '/api/projects/%(_id)s' % project_info
resp = self.client.get(project_url)
project = json.loads(resp.data.decode('utf-8'))
@@ -255,7 +254,7 @@ class ProjectEditTest(AbstractProjectTest):
# Re-fetch from database to see which fields actually made it there.
# equal to put_project -> changed in DB
# equal to project -> not changed in DB
resp = self.client.get('/projects/%s' % project['_id'])
resp = self.client.get('/api/projects/%s' % project['_id'])
db_proj = json.loads(resp.data)
self.assertEqual(put_project['url'], db_proj['url'])
self.assertEqual(put_project['description'], db_proj['description'])
@@ -269,13 +268,13 @@ class ProjectEditTest(AbstractProjectTest):
def test_edits_by_nonowner_admin(self):
"""Any admin should be able to edit any project."""
from application.utils import remove_private_keys, PillarJSONEncoder
from pillar.api.utils import remove_private_keys, PillarJSONEncoder
dumps = functools.partial(json.dumps, cls=PillarJSONEncoder)
# Create test project.
project = self._create_user_and_project([u'subscriber'])
project_id = project['_id']
project_url = '/projects/%s' % project_id
project_url = '/api/projects/%s' % project_id
# Create test user.
self._create_user_with_token(['admin'], 'admin-token', user_id='cafef00dbeef')
@@ -294,13 +293,13 @@ class ProjectEditTest(AbstractProjectTest):
def test_edits_by_nonowner_subscriber(self):
"""A subscriber should only be able to edit their own projects."""
from application.utils import remove_private_keys, PillarJSONEncoder
from pillar.api.utils import remove_private_keys, PillarJSONEncoder
dumps = functools.partial(json.dumps, cls=PillarJSONEncoder)
# Create test project.
project = self._create_user_and_project([u'subscriber'])
project_id = project['_id']
project_url = '/projects/%s' % project_id
project_url = '/api/projects/%s' % project_id
# Create test user.
my_user_id = 'cafef00dbeefcafef00dbeef'
@@ -321,7 +320,7 @@ class ProjectEditTest(AbstractProjectTest):
# Create public test project.
project_info = self._create_user_and_project([u'admin'])
project_id = project_info['_id']
project_url = '/projects/%s' % project_id
project_url = '/api/projects/%s' % project_id
# Create admin user that doesn't own the project, to check that
# non-owner admins can delete projects too.
@@ -347,7 +346,7 @@ class ProjectEditTest(AbstractProjectTest):
# Also see http://python-eve.org/features.html#soft-delete
projection = json.dumps({'name': 1, 'permissions': 1})
where = json.dumps({'_deleted': True}) # MUST be True, 1 does not work.
resp = self.client.get('/projects?where=%s&projection=%s' %
resp = self.client.get('/api/projects?where=%s&projection=%s' %
(urllib.quote(where), urllib.quote(projection)))
self.assertEqual(200, resp.status_code, resp.data)
@@ -359,7 +358,7 @@ class ProjectEditTest(AbstractProjectTest):
# Create test project.
project_info = self._create_user_and_project([u'subscriber'])
project_id = project_info['_id']
project_url = '/projects/%s' % project_id
project_url = '/api/projects/%s' % project_id
# Create test user.
self._create_user_with_token(['subscriber'], 'mortal-token', user_id='cafef00dbeef')
@@ -381,7 +380,7 @@ class ProjectNodeAccess(AbstractProjectTest):
def setUp(self, **kwargs):
super(ProjectNodeAccess, self).setUp(**kwargs)
from application.utils import PillarJSONEncoder
from pillar.api.utils import PillarJSONEncoder
# Project is created by regular subscriber, so should be private.
self.user_id = self._create_user_with_token([u'subscriber'], 'token')
@@ -407,7 +406,7 @@ class ProjectNodeAccess(AbstractProjectTest):
}
# Add a node to the project
resp = self.client.post('/nodes',
resp = self.client.post('/api/nodes',
headers={'Authorization': self.make_header('token'),
'Content-Type': 'application/json'},
data=json.dumps(self.test_node, cls=PillarJSONEncoder),
@@ -415,7 +414,7 @@ class ProjectNodeAccess(AbstractProjectTest):
self.assertEqual(201, resp.status_code, (resp.status_code, resp.data))
self.node_info = json.loads(resp.data)
self.node_id = self.node_info['_id']
self.node_url = '/nodes/%s' % self.node_id
self.node_url = '/api/nodes/%s' % self.node_id
def test_node_access(self):
"""Getting nodes should adhere to project access rules."""
@@ -432,23 +431,23 @@ class ProjectNodeAccess(AbstractProjectTest):
def test_node_resource_access(self):
# The owner of the project should get the node.
resp = self.client.get('/nodes',
resp = self.client.get('/api/nodes',
headers={'Authorization': self.make_header('token')})
self.assertEqual(200, resp.status_code, (resp.status_code, resp.data))
listed_nodes = json.loads(resp.data)['_items']
self.assertEquals(self.node_id, listed_nodes[0]['_id'])
# Listing all nodes should not include nodes from private projects.
resp = self.client.get('/nodes',
resp = self.client.get('/api/nodes',
headers={'Authorization': self.make_header('other-token')})
self.assertEqual(403, resp.status_code, (resp.status_code, resp.data))
def test_is_private_updated_by_world_permissions(self):
"""For backward compatibility, is_private should reflect absence of world-GET"""
from application.utils import remove_private_keys, dumps
from pillar.api.utils import remove_private_keys, dumps
project_url = '/projects/%s' % self.project_id
project_url = '/api/projects/%s' % self.project_id
put_project = remove_private_keys(self.project)
# Create admin user.
@@ -488,10 +487,10 @@ class ProjectNodeAccess(AbstractProjectTest):
self.assertTrue(db_proj['is_private'])
def test_add_remove_user(self):
from application.modules import projects
from application.utils import dumps
from pillar.api.projects import utils as proj_utils
from pillar.api.utils import dumps
project_mng_user_url = '/p/users'
project_mng_user_url = '/api/p/users'
# Use our API to add user to group
payload = {
@@ -512,7 +511,7 @@ class ProjectNodeAccess(AbstractProjectTest):
users = self.app.data.driver.db['users']
db_user = users.find_one(self.other_user_id)
admin_group = projects.get_admin_group(self.project)
admin_group = proj_utils.get_admin_group(self.project)
self.assertIn(admin_group['_id'], db_user['groups'])
@@ -539,10 +538,10 @@ class ProjectNodeAccess(AbstractProjectTest):
regardless of permissions.
"""
from application.modules import projects
from application.utils import dumps
from pillar.api.projects import utils as proj_utils
from pillar.api.utils import dumps
project_mng_user_url = '/p/users'
project_mng_user_url = '/api/p/users'
# Use our API to add user to group
payload = {
@@ -570,5 +569,5 @@ class ProjectNodeAccess(AbstractProjectTest):
users = self.app.data.driver.db['users']
db_user = users.find_one(self.other_user_id)
admin_group = projects.get_admin_group(self.project)
admin_group = proj_utils.get_admin_group(self.project)
self.assertNotIn(admin_group['_id'], db_user['groups'])

View File

@@ -1,13 +1,13 @@
"""Test badger service."""
from common_test_class import AbstractPillarTest, TEST_EMAIL_ADDRESS
from pillar.tests import AbstractPillarTest, TEST_EMAIL_ADDRESS
class BadgerServiceTest(AbstractPillarTest):
def setUp(self, **kwargs):
AbstractPillarTest.setUp(self, **kwargs)
from application.modules import service
from pillar.api import service
with self.app.test_request_context():
self.badger, token_doc = service.create_service_account(
@@ -20,8 +20,8 @@ class BadgerServiceTest(AbstractPillarTest):
self.user_email = TEST_EMAIL_ADDRESS
def _post(self, data):
from application.utils import dumps
return self.client.post('/service/badger',
from pillar.api.utils import dumps
return self.client.post('/api/service/badger',
data=dumps(data),
headers={'Authorization': self.make_header(self.badger_token),
'Content-Type': 'application/json'})

View File

@@ -1,14 +1,13 @@
# -*- encoding: utf-8 -*-
from bson import ObjectId
from pillar.tests import AbstractPillarTest
from werkzeug.exceptions import BadRequest
from common_test_class import AbstractPillarTest
class Str2idTest(AbstractPillarTest):
def test_happy(self):
from application.utils import str2id
from pillar.api.utils import str2id
def happy(str_id):
self.assertEqual(ObjectId(str_id), str2id(str_id))
@@ -18,7 +17,7 @@ class Str2idTest(AbstractPillarTest):
happy(u'577e23ad98377323f74c368c')
def test_unhappy(self):
from application.utils import str2id
from pillar.api.utils import str2id
def unhappy(str_id):
self.assertRaises(BadRequest, str2id, str_id)

108
tests/test_sdk.py Normal file
View File

@@ -0,0 +1,108 @@
"""Tests for the FlaskInternal SDK."""
from flask import url_for
import pillarsdk
from pillar.tests import AbstractPillarTest
from pillar.sdk import FlaskInternalApi
class FlaskInternalApiTest(AbstractPillarTest):
def setUp(self, **kwargs):
AbstractPillarTest.setUp(self, **kwargs)
self.project_id, self.user_id = self.create_project_with_admin()
self.create_valid_auth_token(self.user_id, 'token')
self.sdk_api = FlaskInternalApi(
endpoint='/api/',
username=None,
password=None,
token='token'
)
def test_create_asset(self):
with self.app.test_request_context():
asset = pillarsdk.Node({'description': '',
'project': str(self.project_id),
'node_type': 'asset',
'user': str(self.user_id),
'properties': {'status': 'published',
'content_type': 'je moeder'},
'name': 'Test asset'})
created_ok = asset.create(api=self.sdk_api)
self.assertTrue(created_ok)
self.assertTrue(asset._id)
with self.app.test_request_context():
# Check the asset in MongoDB
resp = self.get(url_for('nodes|item_lookup', _id=asset._id), auth_token='token')
db_asset = resp.json()
self.assertEqual('Test asset', db_asset['name'])
return asset
def test_delete_asset(self):
asset = self.test_create_asset()
with self.app.test_request_context():
asset.delete(api=self.sdk_api)
def test_upload_file_to_project(self):
import test_api
from os.path import join, dirname, abspath
file_path = join(dirname(abspath(test_api.__file__)), 'BlenderDesktopLogo.png')
with self.app.test_request_context():
resp = pillarsdk.File.upload_to_project(
self.project_id,
'image/png',
file_path,
api=self.sdk_api
)
file_id = resp['file_id']
self.assertTrue(file_id)
# Check the file in MongoDB
with self.app.test_request_context():
resp = self.get(url_for('files|item_lookup', _id=file_id), auth_token='token')
file_doc = resp.json()
self.assertEqual('BlenderDesktopLogo.png', file_doc['filename'])
def test_create_asset_from_file(self):
import test_api
from os.path import join, dirname, abspath
file_path = join(dirname(abspath(test_api.__file__)), 'BlenderDesktopLogo.png')
# Create a group node to serve as parent.
with self.app.test_request_context():
resp = self.post(url_for('nodes|resource'), auth_token='token',
json={
'name': 'Group node',
'node_type': 'group',
'project': self.project_id,
'properties': {}
},
expected_status=201)
parent_id = resp.json()['_id']
with self.app.test_request_context(), open(file_path, 'rb') as fileobj:
resp = pillarsdk.Node.create_asset_from_file(
unicode(self.project_id),
unicode(parent_id),
'image',
file_path,
mimetype='image/jpeg',
always_create_new_node=False,
fileobj=fileobj,
api=self.sdk_api)
node_id = resp._id
self.assertTrue(node_id)
# Check the node in MongoDB
with self.app.test_request_context():
resp = self.get(url_for('nodes|item_lookup', _id=node_id), auth_token='token')
node_doc = resp.json()
self.assertEqual('BlenderDesktopLogo.png', node_doc['name'])