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:
Aleksey Ripinen 2015-03-06 15:10:19 +03:00
parent 4ef8732f8f
commit f80bb9c830
11 changed files with 505 additions and 115 deletions

View File

@ -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)

View File

@ -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:

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 = {

View File

@ -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

View File

@ -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):

View File

@ -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.