Branches, milestones and projects ids validation in tasks
Added new rules to set branch_id, project_id and milestone_id int tasks: now all tasks must have project_id and branch id; defaults tasks have master branch id of corresponding project; tasks can't be assign to expired branch or milestone; tasks must be assigned to branch in the same project; tasks must be assigned to milestone in the same project. Now users can't change project_id in branches and branch id in milestones. Change-Id: I29231e32705144a3b575aece7cba00aeb6478a26
This commit is contained in:
parent
4ef8732f8f
commit
f80bb9c830
@ -30,6 +30,7 @@ from storyboard.api.v1.search import search_engine
|
||||
from storyboard.api.v1 import validations
|
||||
from storyboard.api.v1 import wmodels
|
||||
from storyboard.common import decorators
|
||||
from storyboard.common import exception as exc
|
||||
from storyboard.db.api import branches as branches_api
|
||||
from storyboard.openstack.common.gettextutils import _ # noqa
|
||||
|
||||
@ -60,7 +61,7 @@ class BranchesController(rest.RestController):
|
||||
if branch:
|
||||
return wmodels.Branch.from_db_model(branch)
|
||||
else:
|
||||
abort(404, _("Branch %s not found") % branch_id)
|
||||
raise exc.NotFound(_("Branch %s not found") % branch_id)
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.guest)
|
||||
@ -146,9 +147,19 @@ class BranchesController(rest.RestController):
|
||||
else:
|
||||
branch_dict["expiration_date"] = None
|
||||
|
||||
if branch.project_id:
|
||||
original_branch = branches_api.branch_get(branch_id)
|
||||
|
||||
if not original_branch:
|
||||
raise exc.NotFound(_("Branch %s not found") % branch_id)
|
||||
|
||||
if branch.project_id != original_branch.project_id:
|
||||
abort(400, _("You can't associate branch %s "
|
||||
"with another project.") % branch_id)
|
||||
|
||||
result = branches_api.branch_update(branch_id, branch_dict)
|
||||
|
||||
if result:
|
||||
return wmodels.Branch.from_db_model(result)
|
||||
else:
|
||||
abort(404, _("Branch %s not found") % branch_id)
|
||||
raise exc.NotFound(_("Branch %s not found") % branch_id)
|
||||
|
@ -143,6 +143,16 @@ class MilestonesController(rest.RestController):
|
||||
else:
|
||||
milestone_dict["expiration_date"] = None
|
||||
|
||||
if milestone.branch_id:
|
||||
original_milestone = milestones_api.milestone_get(milestone_id)
|
||||
|
||||
if not original_milestone:
|
||||
raise exc.NotFound(_("Milestone %s not found") % milestone_id)
|
||||
|
||||
if milestone.branch_id != original_milestone.branch_id:
|
||||
abort(400, _("You can't associate milestone %s "
|
||||
"with another branch.") % milestone_id)
|
||||
|
||||
result = milestones_api.milestone_update(milestone_id, milestone_dict)
|
||||
|
||||
if result:
|
||||
|
@ -28,6 +28,7 @@ from storyboard.api.v1 import validations
|
||||
from storyboard.api.v1 import wmodels
|
||||
from storyboard.common import decorators
|
||||
from storyboard.common import exception as exc
|
||||
from storyboard.db.api import branches as branches_api
|
||||
from storyboard.db.api import milestones as milestones_api
|
||||
from storyboard.db.api import tasks as tasks_api
|
||||
from storyboard.db.api import timeline_events as events_api
|
||||
@ -38,15 +39,133 @@ CONF = cfg.CONF
|
||||
SEARCH_ENGINE = search_engine.get_engine()
|
||||
|
||||
|
||||
def milestone_is_valid(milestone_id):
|
||||
milestone = milestones_api.milestone_get(milestone_id)
|
||||
def milestone_is_valid(task):
|
||||
"""Check that milestone exists, milestone and task associated with the
|
||||
same branch and milestone is not expired.
|
||||
"""
|
||||
|
||||
if not milestone:
|
||||
raise exc.NotFound(_("Milestone %d not found.") %
|
||||
milestone_id)
|
||||
milestone_id = task.milestone_id
|
||||
milestone = milestones_api.milestone_get(milestone_id)
|
||||
|
||||
if milestone['expired']:
|
||||
abort(400, _("Can't associate task to expired milestone."))
|
||||
if not milestone:
|
||||
raise exc.NotFound(_("Milestone %s not found.") %
|
||||
milestone_id)
|
||||
|
||||
if milestone['expired']:
|
||||
abort(400, _("You can't associate task to expired milestone %s.")
|
||||
% milestone_id)
|
||||
|
||||
if task.branch_id and milestone.branch_id != task.branch_id:
|
||||
abort(400, _("Milestone %(m_id)s doesn't associate "
|
||||
"with branch %(b_id)s.")
|
||||
% {'m_id': milestone_id, 'b_id': task.branch_id})
|
||||
|
||||
|
||||
def branch_is_valid(task):
|
||||
"""Check that branch exists, branch and task associated with the same
|
||||
project and branch is not expired.
|
||||
"""
|
||||
|
||||
branch = branches_api.branch_get(task.branch_id)
|
||||
|
||||
if not branch:
|
||||
raise exc.NotFound(_("Branch %s not found.") % task.branch_id)
|
||||
|
||||
if branch.project_id != task.project_id:
|
||||
abort(400, _("Branch %(b_id)s doesn't associate with "
|
||||
"project %(p_id)s.")
|
||||
% {'b_id': branch.id, 'p_id': task.project_id})
|
||||
|
||||
if branch["expired"]:
|
||||
abort(400, _("You can't associate task with expired branch %s.") %
|
||||
task.branch_id)
|
||||
|
||||
|
||||
def task_is_valid_post(task):
|
||||
"""Check that task can be created.
|
||||
"""
|
||||
|
||||
# Check that task author didn't change creator_id.
|
||||
if task.creator_id and task.creator_id != request.current_user_id:
|
||||
abort(400, _("You can't select author of task."))
|
||||
|
||||
# Check that project_id is in request
|
||||
if not task.project_id:
|
||||
abort(400, _("You must select a project for task."))
|
||||
|
||||
# Set branch_id to 'master' branch defaults and check that
|
||||
# branch is valid for this task.
|
||||
if not task.branch_id:
|
||||
task.branch_id = branches_api.branch_get_master_branch(
|
||||
task.project_id
|
||||
).id
|
||||
else:
|
||||
branch_is_valid(task)
|
||||
|
||||
# Check that task status is merged and milestone is valid for this task
|
||||
# if milestone_id is in request.
|
||||
if task.milestone_id:
|
||||
if task.status != 'merged':
|
||||
abort(400,
|
||||
_("Milestones can only be associated with merged tasks"))
|
||||
|
||||
milestone_is_valid(task)
|
||||
|
||||
return task
|
||||
|
||||
|
||||
def task_is_valid_put(task, original_task):
|
||||
"""Check that task can be update.
|
||||
"""
|
||||
|
||||
# Check that creator_id of task can't be changed.
|
||||
if task.creator_id and task.creator_id != original_task.creator_id:
|
||||
abort(400, _("You can't change author of task."))
|
||||
|
||||
# Set project_id if it isn't in request.
|
||||
if not task.project_id:
|
||||
task.project_id = original_task.project_id
|
||||
|
||||
# Set branch_id if it isn't in request.
|
||||
if not task.branch_id:
|
||||
task.branch_id = original_task.branch_id
|
||||
|
||||
# Check that branch is valid for this task. If project_id was changed,
|
||||
# task will be associated with master branch of this project, because
|
||||
# client doesn't support branches.
|
||||
if task.project_id == original_task.project_id:
|
||||
branch_is_valid(task)
|
||||
else:
|
||||
task.branch_id = branches_api.branch_get_master_branch(
|
||||
task.project_id
|
||||
).id
|
||||
|
||||
# Check that task ready to associate with milestone if milestone_id in
|
||||
# request.
|
||||
if task.milestone_id:
|
||||
if original_task.status != 'merged' and task.status != 'merged':
|
||||
abort(400,
|
||||
_("Milestones can only be associated with merged tasks"))
|
||||
|
||||
if (original_task.status == 'merged' and
|
||||
task.status and task.status != 'merged'):
|
||||
abort(400,
|
||||
_("Milestones can only be associated with merged tasks"))
|
||||
elif 'milestone_id' in task.as_dict(omit_unset=True):
|
||||
return task
|
||||
|
||||
# Set milestone id if task status isn't 'merged' or if original task
|
||||
# has milestone_id.
|
||||
if task.status and task.status != 'merged':
|
||||
task.milestone_id = None
|
||||
elif not task.milestone_id and original_task.milestone_id:
|
||||
task.milestone_id = original_task.milestone_id
|
||||
|
||||
# Check that milestone is valid for this task.
|
||||
if task.milestone_id:
|
||||
milestone_is_valid(task)
|
||||
|
||||
return task
|
||||
|
||||
|
||||
def post_timeline_events(original_task, updated_task):
|
||||
@ -190,18 +309,10 @@ class TasksPrimaryController(rest.RestController):
|
||||
def post(self, task):
|
||||
"""Create a new task.
|
||||
|
||||
:param task: A task within the request body.
|
||||
:param task: a task within the request body.
|
||||
"""
|
||||
|
||||
if task.creator_id and task.creator_id != request.current_user_id:
|
||||
abort(400, _("You can't select author of task."))
|
||||
|
||||
if task.milestone_id:
|
||||
if task.status != 'merged':
|
||||
abort(400,
|
||||
_("Milestones can only be associated with merged tasks"))
|
||||
|
||||
milestone_is_valid(task.milestone_id)
|
||||
task = task_is_valid_post(task)
|
||||
|
||||
creator_id = request.current_user_id
|
||||
task.creator_id = creator_id
|
||||
@ -227,33 +338,16 @@ class TasksPrimaryController(rest.RestController):
|
||||
|
||||
original_task = tasks_api.task_get(task_id)
|
||||
|
||||
if task.creator_id and task.creator_id != original_task.creator_id:
|
||||
abort(400, _("You can't change author of task."))
|
||||
if not original_task:
|
||||
raise exc.NotFound(_("Task %s not found.") % task_id)
|
||||
|
||||
if task.milestone_id:
|
||||
if original_task['status'] != 'merged' and task.status != 'merged':
|
||||
abort(400,
|
||||
_("Milestones can only be associated with merged tasks"))
|
||||
task = task_is_valid_put(task, original_task)
|
||||
|
||||
if (original_task['status'] == 'merged' and
|
||||
task.status and task.status != 'merged'):
|
||||
abort(400,
|
||||
_("Milestones can only be associated with merged tasks"))
|
||||
updated_task = tasks_api.task_update(task_id, task.as_dict(
|
||||
omit_unset=True))
|
||||
|
||||
milestone_is_valid(task.milestone_id)
|
||||
|
||||
task_dict = task.as_dict(omit_unset=True)
|
||||
|
||||
if task.status and task.status != 'merged':
|
||||
task_dict['milestone_id'] = None
|
||||
|
||||
updated_task = tasks_api.task_update(task_id, task_dict)
|
||||
|
||||
if updated_task:
|
||||
post_timeline_events(original_task, updated_task)
|
||||
return wmodels.Task.from_db_model(updated_task)
|
||||
else:
|
||||
raise exc.NotFound(_("Task %s not found") % task_id)
|
||||
post_timeline_events(original_task, updated_task)
|
||||
return wmodels.Task.from_db_model(updated_task)
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.authenticated)
|
||||
@ -394,24 +488,16 @@ class TasksNestedController(rest.RestController):
|
||||
:param task: a task within the request body.
|
||||
"""
|
||||
|
||||
if task.creator_id and task.creator_id != request.current_user_id:
|
||||
abort(400, _("You can't select author of task."))
|
||||
|
||||
creator_id = request.current_user_id
|
||||
task.creator_id = creator_id
|
||||
|
||||
if not task.story_id:
|
||||
task.story_id = story_id
|
||||
|
||||
if task.story_id != story_id:
|
||||
abort(400, _("URL story_id and task.story_id do not match"))
|
||||
|
||||
if task.milestone_id:
|
||||
if task.status != 'merged':
|
||||
abort(400,
|
||||
_("Milestones can only be associated with merged tasks"))
|
||||
task = task_is_valid_post(task)
|
||||
|
||||
milestone_is_valid(task.milestone_id)
|
||||
creator_id = request.current_user_id
|
||||
task.creator_id = creator_id
|
||||
|
||||
created_task = tasks_api.task_create(task.as_dict())
|
||||
|
||||
@ -435,36 +521,19 @@ class TasksNestedController(rest.RestController):
|
||||
|
||||
original_task = tasks_api.task_get(task_id)
|
||||
|
||||
if not original_task:
|
||||
raise exc.NotFound(_("Task %s not found") % task_id)
|
||||
|
||||
if original_task.story_id != story_id:
|
||||
abort(400, _("URL story_id and task.story_id do not match"))
|
||||
|
||||
if task.creator_id and task.creator_id != original_task.creator_id:
|
||||
abort(400, _("You can't change author of task."))
|
||||
task = task_is_valid_put(task, original_task)
|
||||
|
||||
if task.milestone_id:
|
||||
if original_task['status'] != 'merged' and task.status != 'merged':
|
||||
abort(400,
|
||||
_("Milestones can only be associated with merged tasks"))
|
||||
updated_task = tasks_api.task_update(task_id, task.as_dict(
|
||||
omit_unset=True))
|
||||
|
||||
if (original_task['status'] == 'merged' and
|
||||
task.status and task.status != 'merged'):
|
||||
abort(400,
|
||||
_("Milestones can only be associated with merged tasks"))
|
||||
|
||||
milestone_is_valid(task.milestone_id)
|
||||
|
||||
task_dict = task.as_dict(omit_unset=True)
|
||||
|
||||
if task.status and task.status != 'merged':
|
||||
task_dict['milestone_id'] = None
|
||||
|
||||
updated_task = tasks_api.task_update(task_id, task_dict)
|
||||
|
||||
if updated_task:
|
||||
post_timeline_events(original_task, updated_task)
|
||||
return wmodels.Task.from_db_model(updated_task)
|
||||
else:
|
||||
raise exc.NotFound(_("Task %s not found") % task_id)
|
||||
post_timeline_events(original_task, updated_task)
|
||||
return wmodels.Task.from_db_model(updated_task)
|
||||
|
||||
@decorators.db_exceptions
|
||||
@secure(checks.authenticated)
|
||||
|
@ -13,8 +13,10 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from storyboard.common import exception as exc
|
||||
from storyboard.db.api import base as api_base
|
||||
from storyboard.db import models
|
||||
from storyboard.openstack.common.gettextutils import _ # noqa
|
||||
|
||||
|
||||
def branch_get(branch_id):
|
||||
@ -67,3 +69,14 @@ def branch_build_query(project_group_id, **kwargs):
|
||||
**kwargs)
|
||||
|
||||
return query
|
||||
|
||||
|
||||
def branch_get_master_branch(project_id):
|
||||
query = api_base.model_query(models.Branch)
|
||||
query = query.filter_by(project_id=project_id, name='master').first()
|
||||
|
||||
if not query:
|
||||
raise exc.NotFound(_("Master branch of project %d not found.")
|
||||
% project_id)
|
||||
|
||||
return query
|
||||
|
@ -0,0 +1,72 @@
|
||||
# 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.
|
||||
#
|
||||
|
||||
"""This migration fix project_id and branch_id fields in tasks. All tasks
|
||||
without project id now are assigned to project with the smallest id. All tasks
|
||||
without branch_id now assigned to masted branch of matching project.
|
||||
|
||||
Revision ID: 043
|
||||
Revises: 042
|
||||
Create Date: 2015-03-24 13:11:22
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
|
||||
revision = '043'
|
||||
down_revision = '042'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.sql.expression import table
|
||||
|
||||
MYSQL_ENGINE = 'InnoDB'
|
||||
MYSQL_CHARSET = 'utf8'
|
||||
|
||||
|
||||
def upgrade(active_plugins=None, options=None):
|
||||
bind = op.get_bind()
|
||||
|
||||
branches = list(bind.execute(
|
||||
sa.select(columns=['id', 'name', 'project_id'],
|
||||
from_obj=sa.Table('branches', sa.MetaData()))))
|
||||
|
||||
projects = list(bind.execute(
|
||||
sa.select(columns=['id'], from_obj=sa.Table('projects',
|
||||
sa.MetaData()))))
|
||||
if len(projects) > 0:
|
||||
branch_dict = {}
|
||||
|
||||
for branch in branches:
|
||||
branch_dict[(branch['project_id'], branch['name'])] = branch['id']
|
||||
|
||||
tasks_table = table(
|
||||
'tasks',
|
||||
sa.Column('project_id', sa.Integer(), nullable=True),
|
||||
sa.Column('branch_id', sa.Integer(), nullable=True),
|
||||
)
|
||||
|
||||
bind.execute(tasks_table.update().
|
||||
where(tasks_table.c.project_id.is_(None)).
|
||||
values(project_id=projects[0].id))
|
||||
|
||||
for project in projects:
|
||||
bind.execute(
|
||||
tasks_table.update().
|
||||
where(tasks_table.c.project_id == project['id']).
|
||||
values(branch_id=branch_dict[(project['id'], "master")])
|
||||
)
|
||||
|
||||
|
||||
def downgrade(active_plugins=None, options=None):
|
||||
pass
|
@ -22,7 +22,9 @@ import six
|
||||
from sqlalchemy.exc import SADeprecationWarning
|
||||
|
||||
from storyboard.common.custom_types import NameType
|
||||
from storyboard.common.master_branch_helper import MasterBranchHelper
|
||||
from storyboard.db.api import base as db_api
|
||||
from storyboard.db.models import Branch
|
||||
from storyboard.db.models import Project
|
||||
from storyboard.db.models import ProjectGroup
|
||||
from storyboard.openstack.common.gettextutils import _LW # noqa
|
||||
@ -115,6 +117,14 @@ def _get_project(project, session):
|
||||
|
||||
session.add(db_project)
|
||||
|
||||
master_branch = session.query(Branch).\
|
||||
filter_by(name='master', project_id=db_project.id).first()
|
||||
|
||||
if not master_branch:
|
||||
master_branch = Branch()
|
||||
master_branch.update(MasterBranchHelper(db_project.id).as_dict())
|
||||
session.add(master_branch)
|
||||
|
||||
return db_project
|
||||
|
||||
|
||||
|
@ -38,7 +38,7 @@ class TestBranches(base.FunctionalTest):
|
||||
}
|
||||
|
||||
self.put_branch_01 = {
|
||||
'project_id': 2
|
||||
'name': 'new_branch_name'
|
||||
}
|
||||
|
||||
self.put_branch_02 = {
|
||||
@ -54,11 +54,6 @@ class TestBranches(base.FunctionalTest):
|
||||
'expiration_date': '2014-01-01T00:00:00+00:00'
|
||||
}
|
||||
|
||||
self.project_01 = {
|
||||
'name': 'project-for-put',
|
||||
'description': 'test_description'
|
||||
}
|
||||
|
||||
self.default_headers['Authorization'] = 'Bearer valid_superuser_token'
|
||||
|
||||
def test_create(self):
|
||||
@ -84,20 +79,11 @@ class TestBranches(base.FunctionalTest):
|
||||
self.assertIn("id", branch)
|
||||
resource = "".join([self.resource, ("/%d" % branch['id'])])
|
||||
|
||||
response_project = self.post_json('/projects', self.project_01)
|
||||
project = response_project.json
|
||||
|
||||
self.assertEqual(self.project_01['name'], project['name'])
|
||||
self.assertEqual(self.project_01['description'],
|
||||
project['description'])
|
||||
self.assertIn("id", project)
|
||||
self.put_branch_01["id"] = project["id"]
|
||||
|
||||
response = self.put_json(resource, self.put_branch_01)
|
||||
branch = response.json
|
||||
self.assertEqual(branch['name'], self.branch_01['name'])
|
||||
self.assertEqual(branch['name'], self.put_branch_01['name'])
|
||||
self.assertEqual(branch['project_id'],
|
||||
self.put_branch_01['project_id'])
|
||||
self.branch_01['project_id'])
|
||||
|
||||
response = self.put_json(resource, self.put_branch_02)
|
||||
branch = response.json
|
||||
@ -126,6 +112,18 @@ class TestBranches(base.FunctionalTest):
|
||||
expect_errors=True)
|
||||
self.assertEqual(response.status_code, 400)
|
||||
|
||||
def test_change_project(self):
|
||||
response = self.post_json(self.resource, self.branch_01)
|
||||
branch = response.json
|
||||
self.assertIn("id", branch)
|
||||
self.assertEqual(branch['name'], self.branch_01['name'])
|
||||
self.assertEqual(branch['project_id'], self.branch_01['project_id'])
|
||||
resource = "".join([self.resource, ("/%d" % branch['id'])])
|
||||
|
||||
response = self.put_json(resource, {'project_id': 2},
|
||||
expect_errors=True)
|
||||
self.assertEqual(400, response.status_code)
|
||||
|
||||
def test_get_one(self):
|
||||
response = self.post_json(self.resource, self.branch_01)
|
||||
branch = response.json
|
||||
|
@ -357,21 +357,25 @@ class TestTasks(base.FunctionalTest):
|
||||
|
||||
self.task_01 = {
|
||||
'title': 'jsonschema_task_01',
|
||||
'story_id': 1
|
||||
'story_id': 1,
|
||||
'project_id': 1
|
||||
}
|
||||
|
||||
self.task_02 = {
|
||||
'title': 'ts',
|
||||
'story_id': 1
|
||||
'story_id': 1,
|
||||
'project_id': 1
|
||||
}
|
||||
|
||||
self.task_03 = {
|
||||
'title': LONG_STRING,
|
||||
'story_id': 1
|
||||
'story_id': 1,
|
||||
'project_id': 1
|
||||
}
|
||||
|
||||
self.task_04 = {
|
||||
'story_id': 1
|
||||
'story_id': 1,
|
||||
'project_id': 1
|
||||
}
|
||||
|
||||
self.put_task_01 = {
|
||||
|
@ -22,7 +22,7 @@ class TestMilestones(base.FunctionalTest):
|
||||
self.resource = '/milestones'
|
||||
|
||||
self.milestone_01 = {
|
||||
'name': 'test_milestone_01',
|
||||
'name': 'test_milestone_1',
|
||||
'branch_id': 1
|
||||
}
|
||||
|
||||
@ -38,7 +38,7 @@ class TestMilestones(base.FunctionalTest):
|
||||
}
|
||||
|
||||
self.put_milestone_01 = {
|
||||
'branch_id': 2
|
||||
'name': 'new_milestone_name'
|
||||
}
|
||||
|
||||
self.put_milestone_02 = {
|
||||
@ -83,9 +83,9 @@ class TestMilestones(base.FunctionalTest):
|
||||
|
||||
response = self.put_json(resource, self.put_milestone_01)
|
||||
milestone = response.json
|
||||
self.assertEqual(milestone['name'], self.milestone_01['name'])
|
||||
self.assertEqual(milestone['name'], self.put_milestone_01['name'])
|
||||
self.assertEqual(milestone['branch_id'],
|
||||
self.put_milestone_01['branch_id'])
|
||||
self.milestone_01['branch_id'])
|
||||
|
||||
response = self.put_json(resource, self.put_milestone_02)
|
||||
milestone = response.json
|
||||
@ -115,6 +115,19 @@ class TestMilestones(base.FunctionalTest):
|
||||
expect_errors=True)
|
||||
self.assertEqual(response.status_code, 400)
|
||||
|
||||
def test_change_branch(self):
|
||||
response = self.post_json(self.resource, self.milestone_01)
|
||||
milestone = response.json
|
||||
self.assertIn("id", milestone)
|
||||
self.assertEqual(milestone['name'], self.milestone_01['name'])
|
||||
self.assertEqual(milestone['branch_id'],
|
||||
self.milestone_01['branch_id'])
|
||||
resource = "".join([self.resource, ("/%d" % milestone['id'])])
|
||||
|
||||
response = self.put_json(resource, {'branch_id': 2},
|
||||
expect_errors=True)
|
||||
self.assertEqual(400, response.status_code)
|
||||
|
||||
def test_get_one(self):
|
||||
response = self.post_json(self.resource, self.milestone_01)
|
||||
milestone = response.json
|
||||
|
@ -21,32 +21,200 @@ class TestTasksPrimary(base.FunctionalTest):
|
||||
def setUp(self):
|
||||
super(TestTasksPrimary, self).setUp()
|
||||
self.resource = '/tasks'
|
||||
self.default_headers['Authorization'] = 'Bearer valid_superuser_token'
|
||||
|
||||
self.task_01 = {
|
||||
'title': 'StoryBoard',
|
||||
'status': 'todo',
|
||||
'story_id': 1
|
||||
'story_id': 1,
|
||||
'project_id': 1
|
||||
}
|
||||
|
||||
self.task_02 = {
|
||||
'title': 'StoryBoard',
|
||||
'status': 'merged',
|
||||
'story_id': 1,
|
||||
'project_id': 1,
|
||||
'branch_id': 1,
|
||||
'milestone_id': 1
|
||||
}
|
||||
|
||||
self.task_03 = {
|
||||
'title': 'StoryBoard',
|
||||
'status': 'todo',
|
||||
'story_id': 1,
|
||||
'project_id': 1,
|
||||
'branch_id': 2
|
||||
}
|
||||
|
||||
self.task_04 = {
|
||||
'title': 'StoryBoard',
|
||||
'status': 'merged',
|
||||
'story_id': 1,
|
||||
'project_id': 1,
|
||||
'branch_id': 1,
|
||||
'milestone_id': 2
|
||||
}
|
||||
|
||||
self.task_05 = {
|
||||
'title': 'StoryBoard',
|
||||
'status': 'todo',
|
||||
'story_id': 1,
|
||||
'project_id': 1,
|
||||
'branch_id': 1,
|
||||
'milestone_id': 1
|
||||
}
|
||||
|
||||
self.updated_task_01 = {
|
||||
'title': 'StoryBoardUpdated'
|
||||
}
|
||||
|
||||
self.updated_task_02 = {
|
||||
'project_id': 2,
|
||||
'branch_id': 2
|
||||
}
|
||||
|
||||
self.updated_task_03 = {
|
||||
'project_id': 2
|
||||
}
|
||||
|
||||
self.updated_task_04 = {
|
||||
'branch_id': 2
|
||||
}
|
||||
|
||||
self.updated_task_05 = {
|
||||
'milestone_id': 2
|
||||
}
|
||||
|
||||
self.updated_task_06 = {
|
||||
'milestone_id': 1
|
||||
}
|
||||
|
||||
self.updated_task_07 = {
|
||||
'project_id': 1,
|
||||
'branch_id': 1
|
||||
}
|
||||
|
||||
self.updated_task_08 = {
|
||||
'project_id': 2,
|
||||
'branch_id': 2,
|
||||
}
|
||||
|
||||
self.helper_branch_01 = {
|
||||
'name': 'test_branch_01',
|
||||
'project_id': 1
|
||||
}
|
||||
self.default_headers['Authorization'] = 'Bearer valid_superuser_token'
|
||||
|
||||
def test_tasks_endpoint(self):
|
||||
response = self.get_json(self.resource)
|
||||
self.assertEqual(4, len(response))
|
||||
|
||||
def test_create(self):
|
||||
result = self.post_json(self.resource, {
|
||||
'title': 'StoryBoard',
|
||||
'status': 'todo',
|
||||
'story_id': 1
|
||||
})
|
||||
result = self.post_json(self.resource, self.task_01)
|
||||
|
||||
# Retrieve the created task
|
||||
created_task = self \
|
||||
.get_json('%s/%d' % (self.resource, result.json['id']))
|
||||
self.assertEqual(result.json['id'], created_task['id'])
|
||||
self.assertEqual('StoryBoard', created_task['title'])
|
||||
self.assertEqual('todo', created_task['status'])
|
||||
self.assertEqual(1, created_task['story_id'])
|
||||
self.assertEqual(self.task_01['title'], created_task['title'])
|
||||
self.assertEqual(self.task_01['status'], created_task['status'])
|
||||
self.assertEqual(self.task_01['story_id'], created_task['story_id'])
|
||||
self.assertEqual(self.task_01['project_id'],
|
||||
created_task['project_id'])
|
||||
self.assertEqual(1, created_task['branch_id'])
|
||||
|
||||
def test_create_merged_task(self):
|
||||
response = self.post_json(self.resource, self.task_02)
|
||||
created_task = response.json
|
||||
|
||||
self.assertEqual(self.task_02['title'], created_task['title'])
|
||||
self.assertEqual(self.task_02['status'], created_task['status'])
|
||||
self.assertEqual(self.task_02['story_id'], created_task['story_id'])
|
||||
self.assertEqual(self.task_02['project_id'],
|
||||
created_task['project_id'])
|
||||
self.assertEqual(self.task_02['branch_id'], created_task['branch_id'])
|
||||
self.assertEqual(self.task_02['milestone_id'],
|
||||
created_task['milestone_id'])
|
||||
|
||||
def test_create_invalid_associations(self):
|
||||
response = self.post_json(self.resource, self.task_03,
|
||||
expect_errors=True)
|
||||
self.assertEqual(400, response.status_code)
|
||||
|
||||
response = self.post_json(self.resource, self.task_04,
|
||||
expect_errors=True)
|
||||
self.assertEqual(400, response.status_code)
|
||||
|
||||
def test_create_invalid_expired(self):
|
||||
response = self.put_json('/branches/1', {'expired': True})
|
||||
branch = response.json
|
||||
self.assertTrue(branch['expired'])
|
||||
response = self.post_json(self.resource, self.task_02,
|
||||
expect_errors=True)
|
||||
self.assertEqual(400, response.status_code)
|
||||
|
||||
response = self.post_json(self.resource, self.task_02,
|
||||
expect_errors=True)
|
||||
self.assertEqual(400, response.status_code)
|
||||
|
||||
def test_create_invalid_with_milestone(self):
|
||||
response = self.post_json(self.resource, self.task_05,
|
||||
expect_errors=True)
|
||||
self.assertEqual(400, response.status_code)
|
||||
|
||||
def test_task_update(self):
|
||||
response = self.put_json(self.resource + '/1', self.updated_task_01)
|
||||
updated_task = response.json
|
||||
self.assertEqual(self.updated_task_01['title'], updated_task['title'])
|
||||
|
||||
def test_task_update_another_project(self):
|
||||
response = self.put_json(self.resource + '/1', self.updated_task_02)
|
||||
updated_task = response.json
|
||||
self.assertEqual(self.updated_task_02['project_id'],
|
||||
updated_task['project_id'])
|
||||
self.assertEqual(self.updated_task_02['branch_id'],
|
||||
updated_task['branch_id'])
|
||||
|
||||
def test_task_update_invalid_associations(self):
|
||||
response = self.put_json(self.resource + '/1', self.updated_task_04,
|
||||
expect_errors=True)
|
||||
self.assertEqual(400, response.status_code)
|
||||
|
||||
response = self.put_json(self.resource + '/2', self.updated_task_05)
|
||||
updated_task = response.json
|
||||
self.assertEqual(self.updated_task_05['milestone_id'],
|
||||
updated_task['milestone_id'])
|
||||
|
||||
response = self.put_json(self.resource + '/2', self.updated_task_06,
|
||||
expect_errors=True)
|
||||
self.assertEqual(400, response.status_code)
|
||||
|
||||
response = self.put_json(self.resource + '/2', self.updated_task_07,
|
||||
expect_errors=True)
|
||||
self.assertEqual(400, response.status_code)
|
||||
|
||||
def test_task_update_invalid_expired(self):
|
||||
response = self.post_json('/branches', {'name': 'expired_branch',
|
||||
'project_id': 1})
|
||||
branch = response.json
|
||||
branch_id = branch['id']
|
||||
|
||||
response = self.put_json('/branches/%s' % branch_id, {'expired': True})
|
||||
branch = response.json
|
||||
|
||||
self.assertEqual(True, branch['expired'])
|
||||
|
||||
response = self.put_json(self.resource + '/1',
|
||||
{'branch_id': branch_id},
|
||||
expect_errors=True)
|
||||
self.assertEqual(400, response.status_code)
|
||||
|
||||
response = self.put_json('/milestones/1', {'expired': True})
|
||||
milestone = response.json
|
||||
self.assertTrue(milestone['expired'])
|
||||
|
||||
response = self.put_json(self.resource + '/1', self.updated_task_06,
|
||||
expect_errors=True)
|
||||
self.assertEqual(400, response.status_code)
|
||||
|
||||
|
||||
class TestTasksNestedController(base.FunctionalTest):
|
||||
@ -57,6 +225,7 @@ class TestTasksNestedController(base.FunctionalTest):
|
||||
self.task_01 = {
|
||||
'title': 'StoryBoard',
|
||||
'status': 'todo',
|
||||
'project_id': 1,
|
||||
'story_id': 1
|
||||
}
|
||||
self.default_headers['Authorization'] = 'Bearer valid_superuser_token'
|
||||
@ -81,6 +250,7 @@ class TestTasksNestedController(base.FunctionalTest):
|
||||
result = self.post_json(self.resource, {
|
||||
'title': 'StoryBoard',
|
||||
'status': 'todo',
|
||||
'project_id': 1,
|
||||
'story_id': 1
|
||||
})
|
||||
|
||||
@ -90,12 +260,14 @@ class TestTasksNestedController(base.FunctionalTest):
|
||||
self.assertEqual(result.json['id'], created_task['id'])
|
||||
self.assertEqual('StoryBoard', created_task['title'])
|
||||
self.assertEqual('todo', created_task['status'])
|
||||
self.assertEqual(1, created_task['project_id'])
|
||||
self.assertEqual(1, created_task['story_id'])
|
||||
|
||||
def test_create_id_in_url(self):
|
||||
result = self.post_json(self.resource, {
|
||||
'title': 'StoryBoard',
|
||||
'status': 'todo',
|
||||
'project_id': 1
|
||||
})
|
||||
# story_id is not set in the body. URL should handle that
|
||||
|
||||
@ -105,6 +277,7 @@ class TestTasksNestedController(base.FunctionalTest):
|
||||
self.assertEqual(result.json['id'], created_task['id'])
|
||||
self.assertEqual('StoryBoard', created_task['title'])
|
||||
self.assertEqual('todo', created_task['status'])
|
||||
self.assertEqual(1, created_task['project_id'])
|
||||
self.assertEqual(1, created_task['story_id'])
|
||||
|
||||
def test_create_error(self):
|
||||
|
@ -20,6 +20,7 @@ from storyboard.db.api import base as db
|
||||
from storyboard.db.models import AccessToken
|
||||
from storyboard.db.models import Branch
|
||||
from storyboard.db.models import Comment
|
||||
from storyboard.db.models import Milestone
|
||||
from storyboard.db.models import Project
|
||||
from storyboard.db.models import ProjectGroup
|
||||
from storyboard.db.models import Story
|
||||
@ -161,6 +162,7 @@ def load():
|
||||
status='inprogress',
|
||||
story_id=1,
|
||||
project_id=1,
|
||||
branch_id=1,
|
||||
assignee_id=2,
|
||||
priority='medium'
|
||||
),
|
||||
@ -171,6 +173,7 @@ def load():
|
||||
status='merged',
|
||||
story_id=1,
|
||||
project_id=2,
|
||||
branch_id=2,
|
||||
assignee_id=1,
|
||||
priority='high'
|
||||
),
|
||||
@ -181,6 +184,7 @@ def load():
|
||||
status='invalid',
|
||||
story_id=1,
|
||||
project_id=3,
|
||||
branch_ud=3,
|
||||
assignee_id=1,
|
||||
priority='low'
|
||||
),
|
||||
@ -191,6 +195,7 @@ def load():
|
||||
status='merged',
|
||||
story_id=2,
|
||||
project_id=2,
|
||||
branch_id=2,
|
||||
assignee_id=1,
|
||||
priority='medium'
|
||||
)
|
||||
@ -308,6 +313,18 @@ def load():
|
||||
)
|
||||
])
|
||||
|
||||
# Load some milestones
|
||||
load_data([
|
||||
Milestone(
|
||||
name='test_milestone_01',
|
||||
branch_id=1
|
||||
),
|
||||
Milestone(
|
||||
name='test_milestone_02',
|
||||
branch_id=2
|
||||
)
|
||||
])
|
||||
|
||||
|
||||
def load_data(data):
|
||||
"""Pre load test data into the database.
|
||||
|
Loading…
x
Reference in New Issue
Block a user