Text fields validation with json schema

Added new dependency.
Added new hook for checking validity of text fields.
Added json schema.

Change-Id: I2bc5778f2dbfd8e9e226df4016224eaf3cf647fe
This commit is contained in:
Aleksey Ripinen 2014-12-25 17:20:18 +03:00 committed by Michael Krotscheck
parent e3a7c7fe3d
commit 95fa52b8de
14 changed files with 716 additions and 24 deletions

View File

@ -1,4 +1,5 @@
pbr>=0.6,!=0.7,<1.0
jsonschema>=2.0.0,<3.0.0
argparse
alembic>=0.4.1
Babel>=1.3

View File

@ -25,6 +25,7 @@ from storyboard.api import config as api_config
from storyboard.api.middleware.cors_middleware import CORSMiddleware
from storyboard.api.middleware import token_middleware
from storyboard.api.middleware import user_id_hook
from storyboard.api.middleware import validation_hook
from storyboard.api.v1.search import impls as search_engine_impls
from storyboard.api.v1.search import search_engine
from storyboard.notifications.notification_hook import NotificationHook
@ -80,7 +81,8 @@ def setup_app(pecan_config=None):
log.setup('storyboard')
hooks = [
user_id_hook.UserIdHook()
user_id_hook.UserIdHook(),
validation_hook.ValidationHook()
]
# Setup token storage

View File

@ -0,0 +1,45 @@
# Copyright (c) 2014 Mirantis Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import jsonschema
from pecan import abort
from pecan import hooks
class ValidationHook(hooks.PecanHook):
def validate(self, json_body, schema):
try:
jsonschema.validate(json_body, schema)
except jsonschema.ValidationError as invalid:
error_field = '.'.join(invalid.path)
abort(400, json_body={"message": invalid.message,
"field": error_field})
def before(self, state):
request = state.request
method = request.method
if method == 'POST':
if hasattr(state.controller.__self__, 'validation_post_schema'):
schema = state.controller.__self__.validation_post_schema
json_body = request.json
self.validate(json_body, schema)
elif method == 'PUT':
if hasattr(state.controller.__self__, 'validation_put_schema'):
schema = state.controller.__self__.validation_put_schema
json_body = request.json
self.validate(json_body, schema)

View File

@ -21,6 +21,7 @@ from wsme.exc import ClientSideError
import wsmeext.pecan as wsme_pecan
import storyboard.api.auth.authorization_checks as checks
from storyboard.api.v1 import validations
from storyboard.api.v1 import wmodels
from storyboard.db.api import project_groups
from storyboard.db.api import projects
@ -81,6 +82,9 @@ class ProjectGroupsController(rest.RestController):
/projects subcontroller
"""
validation_post_schema = validations.PROJECT_GROUPS_POST_SCHEMA
validation_put_schema = validations.PROJECT_GROUPS_PUT_SCHEMA
@secure(checks.guest)
@wsme_pecan.wsexpose(wmodels.ProjectGroup, int)
def get_one(self, project_group_id):

View File

@ -23,6 +23,7 @@ import wsmeext.pecan as wsme_pecan
from storyboard.api.auth import authorization_checks as checks
from storyboard.api.v1.search import search_engine
from storyboard.api.v1 import validations
from storyboard.api.v1 import wmodels
from storyboard.db.api import projects as projects_api
from storyboard.openstack.common.gettextutils import _ # noqa
@ -40,6 +41,9 @@ class ProjectsController(rest.RestController):
_custom_actions = {"search": ["GET"]}
validation_post_schema = validations.PROJECTS_POST_SCHEMA
validation_put_schema = validations.PROJECTS_PUT_SCHEMA
@secure(checks.guest)
@wsme_pecan.wsexpose(wmodels.Project, int)
def get_one_by_id(self, project_id):

View File

@ -26,6 +26,7 @@ from storyboard.api.auth import authorization_checks as checks
from storyboard.api.v1.search import search_engine
from storyboard.api.v1.timeline import CommentsController
from storyboard.api.v1.timeline import TimeLineEventsController
from storyboard.api.v1 import validations
from storyboard.api.v1 import wmodels
from storyboard.db.api import stories as stories_api
from storyboard.db.api import timeline_events as events_api
@ -42,6 +43,9 @@ class StoriesController(rest.RestController):
_custom_actions = {"search": ["GET"]}
validation_post_schema = validations.STORIES_POST_SCHEMA
validation_put_schema = validations.STORIES_PUT_SCHEMA
@secure(checks.guest)
@wsme_pecan.wsexpose(wmodels.Story, int)
def get_one(self, story_id):

View File

@ -23,6 +23,7 @@ import wsmeext.pecan as wsme_pecan
from storyboard.api.auth import authorization_checks as checks
from storyboard.api.v1.search import search_engine
from storyboard.api.v1 import validations
from storyboard.api.v1 import wmodels
from storyboard.db.api import tasks as tasks_api
from storyboard.db.api import timeline_events as events_api
@ -38,6 +39,9 @@ class TasksController(rest.RestController):
_custom_actions = {"search": ["GET"]}
validation_post_schema = validations.TASKS_POST_SCHEMA
validation_put_schema = validations.TASKS_PUT_SCHEMA
@secure(checks.guest)
@wsme_pecan.wsexpose(wmodels.Task, int)
def get_one(self, task_id):

View File

@ -22,10 +22,11 @@ from wsme.exc import ClientSideError
import wsmeext.pecan as wsme_pecan
from storyboard.api.auth import authorization_checks as checks
from storyboard.api.v1 import validations
from storyboard.api.v1 import wmodels
from storyboard.db.api import teams as teams_api
from storyboard.db.api import users as users_api
from storyboard.openstack.common.gettextutils import _ # noqa
from storyboard.openstack.common.gettextutils import _ # noqas
CONF = cfg.CONF
@ -33,7 +34,6 @@ CONF = cfg.CONF
class UsersSubcontroller(rest.RestController):
"""This controller should be used to list, add or remove users from a Team.
"""
@secure(checks.guest)
@wsme_pecan.wsexpose([wmodels.User], int)
def get(self, team_id):
@ -71,6 +71,9 @@ class UsersSubcontroller(rest.RestController):
class TeamsController(rest.RestController):
"""REST controller for Teams."""
validation_post_schema = validations.TEAMS_POST_SCHEMA
validation_put_schema = validations.TEAMS_PUT_SCHEMA
@secure(checks.guest)
@wsme_pecan.wsexpose(wmodels.Team, int)
def get_one_by_id(self, team_id):

View File

@ -22,6 +22,7 @@ import wsme.types as types
import wsmeext.pecan as wsme_pecan
from storyboard.api.auth import authorization_checks as checks
from storyboard.api.v1 import validations
import storyboard.db.api.users as user_api
from storyboard.openstack.common.gettextutils import _ # noqa
from storyboard.openstack.common import log
@ -32,6 +33,8 @@ LOG = log.getLogger(__name__)
class UserPreferencesController(rest.RestController):
validation_post_schema = validations.USER_PREFERENCES_POST_SCHEMA
@secure(checks.authenticated)
@wsme_pecan.wsexpose(types.DictType(unicode, unicode), int)
def get_all(self, user_id):

View File

@ -26,6 +26,7 @@ from storyboard.api.auth import authorization_checks as checks
from storyboard.api.v1.search import search_engine
from storyboard.api.v1.user_preferences import UserPreferencesController
from storyboard.api.v1.user_tokens import UserTokensController
from storyboard.api.v1 import validations
from storyboard.api.v1 import wmodels
from storyboard.db.api import users as users_api
from storyboard.openstack.common.gettextutils import _ # noqa
@ -47,6 +48,9 @@ class UsersController(rest.RestController):
_custom_actions = {"search": ["GET"]}
validation_post_schema = validations.USERS_POST_SCHEMA
validation_put_schema = validations.USERS_PUT_SCHEMA
@secure(checks.guest)
@wsme_pecan.wsexpose([wmodels.User], int, int, unicode, unicode, unicode,
unicode)

View File

@ -0,0 +1,178 @@
# Copyright (c) 2013 Mirantis Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import copy
from storyboard.db.models import CommonLength
USERS_PUT_SCHEMA = {
"name": "user_schema",
"type": "object",
"properties": {
"username": {
"type": "string",
"minLength": CommonLength.lower_middle_length,
"maxLength": CommonLength.name_length
},
"full_name": {
"type": ["string"],
"minLength": CommonLength.lower_middle_length,
"maxLength": CommonLength.top_large_length
},
"email": {
"type": ["string"],
"minLength": CommonLength.lower_large_length,
"maxLength": CommonLength.top_large_length
},
"openid": {
"type": ["string", "null"],
"maxLength": CommonLength.top_large_length
}
}
}
USERS_POST_SCHEMA = copy.deepcopy(USERS_PUT_SCHEMA)
USERS_POST_SCHEMA["required"] = ["username", "full_name", "email"]
USER_PREFERENCES_POST_SCHEMA = {
"name": "userPreference_schema",
"type": "object",
"patternProperties": {
"^.{3,100}$": {
"type": ["string", "boolean", "number", "null"],
"minLength": CommonLength.lower_short_length,
"maxLength": CommonLength.top_large_length
}
},
"additionalProperties": False
}
TEAMS_PUT_SCHEMA = {
"name": "team_schema",
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": CommonLength.lower_middle_length,
"maxLength": CommonLength.top_large_length
}
}
}
TEAMS_POST_SCHEMA = copy.deepcopy(TEAMS_PUT_SCHEMA)
TEAMS_POST_SCHEMA["required"] = ["name"]
"""permission_chema is not applied anywhere until permission controller
is implemented"""
PERMISSIONS_PUT_SCHEMA = {
"name": "permission_schema",
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": CommonLength.lower_middle_length,
"maxLength": CommonLength.top_short_length
},
"codename": {
"type": "string",
"maxLength": CommonLength.top_large_length
}
}
}
PERMISSIONS_POST_SCHEMA = copy.deepcopy(PERMISSIONS_PUT_SCHEMA)
PERMISSIONS_POST_SCHEMA["required"] = ["name", "codename"]
PROJECTS_PUT_SCHEMA = {
"name": "project_schema",
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": CommonLength.lower_large_length,
"maxLength": CommonLength.top_short_length
}
}
}
PROJECTS_POST_SCHEMA = copy.deepcopy(PROJECTS_PUT_SCHEMA)
PROJECTS_POST_SCHEMA["required"] = ["name"]
PROJECT_GROUPS_PUT_SCHEMA = {
"name": "projectGroup_schema",
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": CommonLength.lower_large_length,
"maxLength": CommonLength.top_short_length
},
"title": {
"type": "string",
"minLength": CommonLength.lower_middle_length,
"maxLength": CommonLength.top_large_length
}
}
}
PROJECT_GROUPS_POST_SCHEMA = copy.deepcopy(PROJECT_GROUPS_PUT_SCHEMA)
PROJECT_GROUPS_POST_SCHEMA["required"] = ["name", "title"]
STORIES_PUT_SCHEMA = {
"name": "story_schema",
"type": "object",
"properties": {
"title": {
"type": "string",
"minLength": CommonLength.lower_large_length,
"maxLength": CommonLength.top_large_length,
}
}
}
STORIES_POST_SCHEMA = copy.deepcopy(STORIES_PUT_SCHEMA)
STORIES_POST_SCHEMA["required"] = ["title"]
TASKS_PUT_SCHEMA = {
"name": "task_schema",
"type": "object",
"properties": {
"title": {
"type": "string",
"minLength": CommonLength.lower_middle_length,
"maxLength": CommonLength.top_large_length
}
}
}
TASKS_POST_SCHEMA = copy.deepcopy(TASKS_PUT_SCHEMA)
TASKS_POST_SCHEMA["required"] = ["title"]
STORY_TAGS_PUT_SCHEMA = {
"name": "storyTag_schema",
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": CommonLength.lower_middle_length,
"maxLength": CommonLength.top_short_length
}
}
}
STORY_TAGS_POST_SCHEMA = copy.deepcopy(STORY_TAGS_PUT_SCHEMA)
STORY_TAGS_POST_SCHEMA["required"] = ["name"]

View File

@ -57,6 +57,16 @@ def table_args():
MYSQL_MEDIUM_TEXT = UnicodeText().with_variant(MEDIUMTEXT(), 'mysql')
class CommonLength:
top_large_length = 255
top_middle_length = 100
top_short_length = 50
lower_large_length = 5
lower_middle_length = 3
lower_short_length = 1
name_length = 30
class IdMixin(object):
id = Column(Integer, primary_key=True)
@ -117,10 +127,10 @@ class User(FullText, ModelBuilder, Base):
__fulltext_columns__ = ['username', 'full_name', 'email']
username = Column(Unicode(30))
full_name = Column(Unicode(255), nullable=True)
email = Column(String(255))
openid = Column(String(255))
username = Column(Unicode(CommonLength.name_length))
full_name = Column(Unicode(CommonLength.top_large_length), nullable=True)
email = Column(String(CommonLength.top_large_length))
openid = Column(String(CommonLength.top_large_length))
is_staff = Column(Boolean, default=False)
is_active = Column(Boolean, default=True)
is_superuser = Column(Boolean, default=False)
@ -141,8 +151,8 @@ class UserPreference(ModelBuilder, Base):
_TASK_TYPES = ('string', 'int', 'bool', 'float')
user_id = Column(Integer, ForeignKey('users.id'))
key = Column(Unicode(100))
value = Column(Unicode(255))
key = Column(Unicode(CommonLength.top_middle_length))
value = Column(Unicode(CommonLength.top_large_length))
type = Column(Enum(*_TASK_TYPES), default='string')
@property
@ -179,7 +189,7 @@ class Team(ModelBuilder, Base):
__table_args__ = (
schema.UniqueConstraint('name', name='uniq_team_name'),
)
name = Column(Unicode(255))
name = Column(Unicode(CommonLength.top_large_length))
users = relationship("User", secondary="team_membership")
permissions = relationship("Permission", secondary="team_permissions")
@ -195,8 +205,8 @@ class Permission(ModelBuilder, Base):
__table_args__ = (
schema.UniqueConstraint('name', name='uniq_permission_name'),
)
name = Column(Unicode(50))
codename = Column(Unicode(255))
name = Column(Unicode(CommonLength.top_short_length))
codename = Column(Unicode(CommonLength.top_large_length))
# TODO(mordred): Do we really need name and title?
@ -209,7 +219,7 @@ class Project(FullText, ModelBuilder, Base):
__fulltext_columns__ = ['name', 'description']
name = Column(String(50))
name = Column(String(CommonLength.top_short_length))
description = Column(UnicodeText())
team_id = Column(Integer, ForeignKey('teams.id'))
team = relationship(Team, primaryjoin=team_id == Team.id)
@ -228,8 +238,8 @@ class ProjectGroup(ModelBuilder, Base):
schema.UniqueConstraint('name', name='uniq_group_name'),
)
name = Column(String(50))
title = Column(Unicode(255))
name = Column(String(CommonLength.top_short_length))
title = Column(Unicode(CommonLength.top_large_length))
projects = relationship("Project", secondary="project_group_mapping")
_public_fields = ["id", "name", "title", "projects"]
@ -249,7 +259,7 @@ class Story(FullText, ModelBuilder, Base):
creator_id = Column(Integer, ForeignKey('users.id'))
creator = relationship(User, primaryjoin=creator_id == User.id)
title = Column(Unicode(255))
title = Column(Unicode(CommonLength.top_large_length))
description = Column(UnicodeText())
is_bug = Column(Boolean, default=True)
tasks = relationship('Task', backref='story')
@ -272,7 +282,7 @@ class Task(FullText, ModelBuilder, Base):
_TASK_PRIORITIES = ('low', 'medium', 'high')
creator_id = Column(Integer, ForeignKey('users.id'))
title = Column(Unicode(255), nullable=True)
title = Column(Unicode(CommonLength.top_large_length), nullable=True)
status = Column(Enum(*TASK_STATUSES.keys()), default='todo')
story_id = Column(Integer, ForeignKey('stories.id'))
project_id = Column(Integer, ForeignKey('projects.id'))
@ -288,28 +298,30 @@ class StoryTag(ModelBuilder, Base):
__table_args__ = (
schema.UniqueConstraint('name', name='uniq_story_tags_name'),
)
name = Column(String(50))
name = Column(String(CommonLength.top_short_length))
stories = relationship('StoryTag', secondary='story_storytags')
# Authorization models
class AuthorizationCode(ModelBuilder, Base):
code = Column(Unicode(100), nullable=False)
state = Column(Unicode(100), nullable=False)
code = Column(Unicode(CommonLength.top_middle_length), nullable=False)
state = Column(Unicode(CommonLength.top_middle_length), nullable=False)
user_id = Column(Integer, ForeignKey('users.id'), nullable=False)
class AccessToken(ModelBuilder, Base):
user_id = Column(Integer, ForeignKey('users.id'), nullable=False)
access_token = Column(Unicode(100), nullable=False)
access_token = Column(Unicode(CommonLength.top_middle_length),
nullable=False)
expires_in = Column(Integer, nullable=False)
expires_at = Column(DateTime, nullable=False)
class RefreshToken(ModelBuilder, Base):
user_id = Column(Integer, ForeignKey('users.id'), nullable=False)
refresh_token = Column(Unicode(100), nullable=False)
refresh_token = Column(Unicode(CommonLength.top_middle_length),
nullable=False)
expires_in = Column(Integer, nullable=False)
expires_at = Column(DateTime, nullable=False)
@ -359,7 +371,8 @@ class TimeLineEvent(ModelBuilder, Base):
comment_id = Column(Integer, ForeignKey('comments.id'), nullable=True)
author_id = Column(Integer, ForeignKey('users.id'), nullable=True)
event_type = Column(Unicode(100), nullable=False)
event_type = Column(Unicode(CommonLength.top_middle_length),
nullable=False)
# this info field should contain additional fields to describe the event
# ex. {'old_status': 'Todo', 'new_status': 'In progress'}
@ -393,5 +406,6 @@ class SubscriptionEvents(ModelBuilder, Base):
subscriber_id = Column(Integer, ForeignKey('users.id'))
author_id = Column(Integer, ForeignKey('users.id'))
event_type = Column(Unicode(100), nullable=False)
event_type = Column(Unicode(CommonLength.top_middle_length),
nullable=False)
event_info = Column(UnicodeText(), nullable=True)

View File

@ -0,0 +1,423 @@
# Copyright (c) 2014 Mirantis Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import json
import six
import unittest
from storyboard.tests import base
LONG_STRING = ''.join(['a' for i in range(0, 260)])
def create(test_class, entity, resource):
response = test_class.post_json(resource, entity)
response_body = json.loads(response.body)
for key, value in six.iteritems(entity):
test_class.assertEqual(value, response_body[key])
def create_invalid_length(test_class, entity, resource, field=""):
response = test_class.post_json(resource, entity, expect_errors=True)
response_body = json.loads(response.body)
test_class.assertEqual(400, response.status_code)
test_class.assertEqual(field, response_body["field"])
def create_invalid_required(test_class, entity, resource, field=""):
response = test_class.post_json(resource, entity, expect_errors=True)
response_body = json.loads(response.body)
test_class.assertEqual(400, response.status_code)
test_class.assertEqual(six.text_type('\'%s\' is a required property') %
field, response_body["message"])
def update(test_class, entity, resource):
response = test_class.put_json(resource, entity)
response_body = json.loads(response.body)
for key, value in six.iteritems(entity):
test_class.assertEqual(value, response_body[key])
def update_invalid(test_class, entity, resource, field=""):
response = test_class.put_json(resource, entity, expect_errors=True)
response_body = json.loads(response.body)
test_class.assertEqual(400, response.status_code)
test_class.assertEqual(field, response_body["field"])
class TestUsers(base.FunctionalTest):
def setUp(self):
super(TestUsers, self).setUp()
self.resource = '/users'
self.default_headers['Authorization'] = 'Bearer valid_superuser_token'
self.user_01 = {
'username': 'jsonschema_test_user1',
'full_name': 'jsonschema_test_user1',
'email': 'jsonschema_test_user1@test.ru',
'openid': 'qwerty'
}
self.user_02 = {
'username': 't2',
'full_name': 'jsonschema_test_user2',
'email': 'jsonschema_test_user2@test.ru',
'openid': 'qwertyu'
}
self.user_03 = {
'username': 'jsonschema_test_user3',
'full_name': LONG_STRING,
'email': 'jsonschema_test_user3@test.ru',
'openid': 'qwertyui'
}
self.user_04 = {
'full_name': 'jsonschema_test_user4',
'email': 'jsonschema_test_user4@test.ru',
'openid': 'qwertyuio'
}
self.put_user_01 = {
'id': 2,
'full_name': 'new full_name of regular User'
}
self.put_user_02 = {
'full_name': 'ok'
}
self.put_user_03 = {
'email': LONG_STRING
}
def test_create(self):
create(self, self.user_01, self.resource)
def test_create_invalid(self):
create_invalid_length(self, self.user_02, self.resource, 'username')
create_invalid_length(self, self.user_03, self.resource, 'full_name')
create_invalid_required(self, self.user_04, self.resource, 'username')
@unittest.skip("Method put in UsersController must be modified.")
def test_update(self):
resource = "".join([self.resource, "/2"])
update(self, self.put_user_01, resource)
@unittest.skip("Method put in UsersController must be modified.")
def test_update_invalid(self):
resource = "".join([self.resource, "/2"])
update_invalid(self, self.put_user_02, resource, 'full_name')
update_invalid(self, self.put_user_03, resource, 'email')
class TestProjects(base.FunctionalTest):
def setUp(self):
super(TestProjects, self).setUp()
self.resource = '/projects'
self.default_headers['Authorization'] = 'Bearer valid_superuser_token'
self.project_01 = {
'name': 'jsonschema-project-01',
'description': 'jsonschema_description_01'
}
self.project_02 = {
'name': 'pr',
'description': 'jsonschema_description_02'
}
self.project_03 = {
'name': LONG_STRING,
'description': 'jsonschema_description_03'
}
self.project_04 = {
'description': 'jsonschema_description_04'
}
self.put_project_01 = {
'id': 2,
'description': 'jsonschema_put_description_01'
}
self.put_project_02 = {
'name': 'ok'
}
self.put_project_03 = {
'name': LONG_STRING
}
def test_create(self):
create(self, self.project_01, self.resource)
def test_create_invalid(self):
create_invalid_length(self, self.project_02, self.resource, 'name')
create_invalid_length(self, self.project_03, self.resource, 'name')
create_invalid_required(self, self.project_04, self.resource, 'name')
def test_update(self):
resource = "".join([self.resource, "/2"])
update(self, self.put_project_01, resource)
def test_update_invalid(self):
resource = "".join([self.resource, "/2"])
update_invalid(self, self.put_project_02, resource, 'name')
update_invalid(self, self.put_project_03, resource, 'name')
class TestUserPreferences(base.FunctionalTest):
def setUp(self):
super(TestUserPreferences, self).setUp()
self.resource = '/users/2/preferences'
self.default_headers['Authorization'] = 'Bearer valid_user_token'
self.preferences_01 = {
'stringPref': 'jsonschema_preference_01'
}
self.preferences_02 = {
'stringPref': ''
}
self.preferences_03 = {
'stringPref': LONG_STRING
}
def test_create(self):
create(self, self.preferences_01, self.resource)
def test_create_invalid(self):
create_invalid_length(self, self.preferences_02, self.resource,
'stringPref')
create_invalid_length(self, self.preferences_03, self.resource,
'stringPref')
class TestTeams(base.FunctionalTest):
def setUp(self):
super(TestTeams, self).setUp()
self.resource = '/teams'
self.default_headers['Authorization'] = 'Bearer valid_superuser_token'
self.team_01 = {
'name': 'jsonschema-team-01'
}
self.team_02 = {
'name': 'te'
}
self.team_03 = {
'name': LONG_STRING
}
self.team_04 = {
}
def test_create(self):
create(self, self.team_01, self.resource)
def test_create_invalid(self):
create_invalid_length(self, self.team_02, self.resource,
'name')
create_invalid_length(self, self.team_03, self.resource,
'name')
create_invalid_required(self, self.team_04, self.resource, 'name')
class TestProjectGroups(base.FunctionalTest):
def setUp(self):
super(TestProjectGroups, self).setUp()
self.resource = '/project_groups'
self.default_headers['Authorization'] = 'Bearer valid_superuser_token'
self.project_group_01 = {
'name': 'jsonschema-project-group-01',
'title': 'jsonschema_project_group_title_01'
}
self.project_group_02 = {
'name': 'pr',
'title': 'jsonschema_project_group_title_02'
}
self.project_group_03 = {
'name': 'jsonschema-project-group-03',
'title': LONG_STRING
}
self.project_group_04 = {
'name': 'jsonschema-project-group-04',
}
self.put_project_group_01 = {
'title': 'put_project_group_01'
}
self.put_project_group_02 = {
'title': 'tl'
}
self.put_project_group_03 = {
'title': LONG_STRING
}
def test_create(self):
create(self, self.project_group_01, self.resource)
def test_create_invalid(self):
create_invalid_length(self, self.project_group_02, self.resource,
'name')
create_invalid_length(self, self.project_group_03, self.resource,
'title')
create_invalid_required(self, self.project_group_04, self.resource,
'title')
def test_update(self):
resource = "".join([self.resource, "/2"])
update(self, self.put_project_group_01, resource)
def test_update_invalid(self):
resource = "".join([self.resource, "/2"])
update_invalid(self, self.put_project_group_02, resource, 'title')
update_invalid(self, self.put_project_group_03, resource, 'title')
class TestStories(base.FunctionalTest):
def setUp(self):
super(TestStories, self).setUp()
self.resource = '/stories'
self.default_headers['Authorization'] = 'Bearer valid_superuser_token'
self.story_01 = {
'title': 'jsonschema_story_01',
'description': 'jsonschema_story_description_01'
}
self.story_02 = {
'title': 'st',
'description': 'jsonschema_story_description_02'
}
self.story_03 = {
'title': LONG_STRING,
'description': 'jsonschema_story_description_03'
}
self.story_04 = {
'description': 'jsonschema_story_description_04'
}
self.put_story_01 = {
'title': 'put_story_01'
}
self.put_story_02 = {
'title': 'tl'
}
self.put_story_03 = {
'title': LONG_STRING
}
def test_create(self):
create(self, self.story_01, self.resource)
def test_create_invalid(self):
create_invalid_length(self, self.story_02, self.resource,
'title')
create_invalid_length(self, self.story_03, self.resource,
'title')
create_invalid_required(self, self.story_04, self.resource,
'title')
def test_update(self):
resource = "".join([self.resource, "/2"])
update(self, self.put_story_01, resource)
def test_update_invalid(self):
resource = "".join([self.resource, "/2"])
update_invalid(self, self.put_story_02, resource, 'title')
update_invalid(self, self.put_story_03, resource, 'title')
class TestTasks(base.FunctionalTest):
def setUp(self):
super(TestTasks, self).setUp()
self.resource = '/tasks'
self.default_headers['Authorization'] = 'Bearer valid_superuser_token'
self.task_01 = {
'title': 'jsonschema_task_01',
'story_id': 1
}
self.task_02 = {
'title': 'ts',
'story_id': 1
}
self.task_03 = {
'title': LONG_STRING,
'story_id': 1
}
self.task_04 = {
'story_id': 1
}
self.put_task_01 = {
'title': 'put_task_01'
}
self.put_task_02 = {
'title': 'tl'
}
self.put_task_03 = {
'title': LONG_STRING
}
def test_create(self):
create(self, self.task_01, self.resource)
def test_create_invalid(self):
create_invalid_length(self, self.task_02, self.resource,
'title')
create_invalid_length(self, self.task_03, self.resource,
'title')
create_invalid_required(self, self.task_04, self.resource,
'title')
def test_update(self):
resource = "".join([self.resource, "/2"])
update(self, self.put_task_01, resource)
def test_update_invalid(self):
resource = "".join([self.resource, "/2"])
update_invalid(self, self.put_task_02, resource, 'title')
update_invalid(self, self.put_task_03, resource, 'title')

View File

@ -12,6 +12,8 @@
# License for the specific language governing permissions and limitations
# under the License.
import unittest
from storyboard.db.api import users as user_api
from storyboard.tests import base
@ -22,6 +24,7 @@ class TestUsersAsSuperuser(base.FunctionalTest):
self.resource = '/users'
self.default_headers['Authorization'] = 'Bearer valid_superuser_token'
@unittest.skip("Method put in UsersController must be modified.")
def test_update_enable_login(self):
path = self.resource + '/2'