Merge "Add some tests for checking private story behaviour"
This commit is contained in:
commit
363c64f47c
@ -36,6 +36,24 @@ class TestComments(base.FunctionalTest):
|
|||||||
response = self.get_json(self.comments_resource % self.story_id)
|
response = self.get_json(self.comments_resource % self.story_id)
|
||||||
self.assertEqual(0, len(response))
|
self.assertEqual(0, len(response))
|
||||||
|
|
||||||
|
def test_comments_privacy(self):
|
||||||
|
url = '/stories/6/comments'
|
||||||
|
response = self.get_json(url, expect_errors=True)
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
self.assertEqual(1, len(response.json))
|
||||||
|
|
||||||
|
# The user with token `valid_user_token` can't see the story, and
|
||||||
|
# so shouldn't be able to see the comment
|
||||||
|
headers = {'Authorization': 'Bearer valid_user_token'}
|
||||||
|
response = self.get_json(url, headers=headers, expect_errors=True)
|
||||||
|
self.assertEqual(0, len(response.json))
|
||||||
|
|
||||||
|
# Unauthenticated users shouldn't be able to see anything in private
|
||||||
|
# stories
|
||||||
|
self.default_headers.pop('Authorization')
|
||||||
|
response = self.get_json(url, expect_errors=True)
|
||||||
|
self.assertEqual(0, len(response.json))
|
||||||
|
|
||||||
def test_create(self):
|
def test_create(self):
|
||||||
self.post_json(self.comments_resource % self.story_id, self.comment_01)
|
self.post_json(self.comments_resource % self.story_id, self.comment_01)
|
||||||
self.post_json(self.comments_resource % self.story_id, self.comment_02)
|
self.post_json(self.comments_resource % self.story_id, self.comment_02)
|
||||||
|
@ -33,7 +33,29 @@ class TestStories(base.FunctionalTest):
|
|||||||
|
|
||||||
def test_stories_endpoint(self):
|
def test_stories_endpoint(self):
|
||||||
response = self.get_json(self.resource)
|
response = self.get_json(self.resource)
|
||||||
self.assertEqual(5, len(response))
|
self.assertEqual(6, len(response))
|
||||||
|
|
||||||
|
def test_private_story_visibility(self):
|
||||||
|
url = self.resource + '/6'
|
||||||
|
story = self.get_json(url)
|
||||||
|
|
||||||
|
# User with token `valid_superuser_token` has permission to see
|
||||||
|
# the story, so should be able to get it without issue.
|
||||||
|
self.assertEqual(story['title'], 'Test Private Story')
|
||||||
|
self.assertTrue(story['private'])
|
||||||
|
self.assertEqual(1, len(story['users']))
|
||||||
|
self.assertEqual('Super User', story['users'][0]['full_name'])
|
||||||
|
self.assertEqual(0, len(story['teams']))
|
||||||
|
|
||||||
|
# User with token `valid_user_token` doesn't have permission
|
||||||
|
headers = {'Authorization': 'Bearer valid_user_token'}
|
||||||
|
response = self.get_json(url, headers=headers, expect_errors=True)
|
||||||
|
self.assertEqual(404, response.status_code)
|
||||||
|
|
||||||
|
# Unauthenticated users shouldn't be able to see private stories
|
||||||
|
self.default_headers.pop('Authorization')
|
||||||
|
response = self.get_json(url, expect_errors=True)
|
||||||
|
self.assertEqual(404, response.status_code)
|
||||||
|
|
||||||
def test_create(self):
|
def test_create(self):
|
||||||
response = self.post_json(self.resource, self.story_01)
|
response = self.post_json(self.resource, self.story_01)
|
||||||
@ -62,22 +84,22 @@ class TestStories(base.FunctionalTest):
|
|||||||
self.assertEqual(story['story_type_id'],
|
self.assertEqual(story['story_type_id'],
|
||||||
created_story['story_type_id'])
|
created_story['story_type_id'])
|
||||||
|
|
||||||
@unittest.skip("vulnerabilities are not supported.")
|
def test_create_private_story(self):
|
||||||
def test_create_private_vulnerability(self):
|
|
||||||
story = {
|
story = {
|
||||||
'title': 'StoryBoard',
|
'title': 'StoryBoard',
|
||||||
'description': 'Awesome Task Tracker',
|
'description': 'Awesome Task Tracker',
|
||||||
'story_type_id': 3
|
'private': True,
|
||||||
|
'users': [{'id': 1}]
|
||||||
}
|
}
|
||||||
response = self.post_json(self.resource, story)
|
response = self.post_json(self.resource, story)
|
||||||
created_story = response.json
|
created_story = response.json
|
||||||
|
|
||||||
self.assertEqual(story['title'], created_story['title'])
|
self.assertEqual(story['title'], created_story['title'])
|
||||||
self.assertEqual(story['description'], created_story['description'])
|
self.assertEqual(story['description'], created_story['description'])
|
||||||
self.assertEqual(story['story_type_id'],
|
self.assertEqual(story['private'],
|
||||||
created_story['story_type_id'])
|
created_story['private'])
|
||||||
|
|
||||||
@unittest.skip("vulnerabilities are not supported.")
|
@unittest.skip("public vulnerabilities are not supported.")
|
||||||
def test_create_public_vulnerability(self):
|
def test_create_public_vulnerability(self):
|
||||||
story = {
|
story = {
|
||||||
'title': 'StoryBoard',
|
'title': 'StoryBoard',
|
||||||
@ -129,25 +151,31 @@ class TestStories(base.FunctionalTest):
|
|||||||
{'story_type_id': story_type_id})
|
{'story_type_id': story_type_id})
|
||||||
self.assertEqual(story_type_id, response.json['story_type_id'])
|
self.assertEqual(story_type_id, response.json['story_type_id'])
|
||||||
|
|
||||||
@unittest.skip("vulnerabilities are not supported.")
|
def test_update_private_to_public(self):
|
||||||
def test_update_private_to_public_vulnerability(self):
|
|
||||||
story = {
|
story = {
|
||||||
'title': 'StoryBoard',
|
'title': 'StoryBoard',
|
||||||
'description': 'Awesome Task Tracker',
|
'description': 'Awesome Task Tracker',
|
||||||
'story_type_id': 3
|
'private': True
|
||||||
}
|
}
|
||||||
|
|
||||||
response = self.post_json(self.resource, story)
|
response = self.post_json(self.resource, story)
|
||||||
created_story = response.json
|
created_story = response.json
|
||||||
|
|
||||||
self.assertEqual(story["story_type_id"],
|
self.assertEqual(story['private'],
|
||||||
created_story["story_type_id"])
|
created_story['private'])
|
||||||
|
|
||||||
response = self.put_json(self.resource +
|
response = self.put_json(self.resource +
|
||||||
('/%s' % created_story["id"]),
|
('/%s' % created_story['id']),
|
||||||
{'story_type_id': 4})
|
{'private': False})
|
||||||
created_story = response.json
|
updated_story = response.json
|
||||||
self.assertEqual(4, created_story['story_type_id'])
|
self.assertFalse(updated_story['private'])
|
||||||
|
|
||||||
|
# Check that a different user can see the story
|
||||||
|
headers = {'Authorization': 'Bearer valid_user_token'}
|
||||||
|
api_story = self.get_json(self.resource + '/%s' % created_story['id'],
|
||||||
|
headers=headers)
|
||||||
|
self.assertEqual(story['title'], api_story['title'])
|
||||||
|
self.assertEqual(story['description'], api_story['description'])
|
||||||
|
|
||||||
def test_update_restricted_branches(self):
|
def test_update_restricted_branches(self):
|
||||||
response = self.put_json(self.resource + '/1', {'story_type_id': 2},
|
response = self.put_json(self.resource + '/1', {'story_type_id': 2},
|
||||||
|
@ -107,7 +107,34 @@ class TestTasksPrimary(base.FunctionalTest):
|
|||||||
|
|
||||||
def test_tasks_endpoint(self):
|
def test_tasks_endpoint(self):
|
||||||
response = self.get_json(self.resource)
|
response = self.get_json(self.resource)
|
||||||
|
self.assertEqual(5, len(response))
|
||||||
|
|
||||||
|
# Check that tasks in private stories are correctly filtered
|
||||||
|
headers = {'Authorization': 'Bearer valid_user_token'}
|
||||||
|
response = self.get_json(self.resource, headers=headers)
|
||||||
self.assertEqual(4, len(response))
|
self.assertEqual(4, len(response))
|
||||||
|
self.default_headers.pop('Authorization')
|
||||||
|
response = self.get_json(self.resource)
|
||||||
|
self.assertEqual(4, len(response))
|
||||||
|
|
||||||
|
def test_private_task_visibility(self):
|
||||||
|
url = self.resource + '/5'
|
||||||
|
# Task with id 5 is in a private story which the user with token
|
||||||
|
# `valid_superuser_token` can see
|
||||||
|
response = self.get_json(url)
|
||||||
|
self.assertEqual('Task in private story', response['title'])
|
||||||
|
|
||||||
|
# The user with token `valid_user_token` can't see the story, and
|
||||||
|
# so shouldn't be able to see the task
|
||||||
|
headers = {'Authorization': 'Bearer valid_user_token'}
|
||||||
|
response = self.get_json(url, headers=headers, expect_errors=True)
|
||||||
|
self.assertEqual(404, response.status_code)
|
||||||
|
|
||||||
|
# Unauthenticated users shouldn't be able to see anything in private
|
||||||
|
# stories
|
||||||
|
self.default_headers.pop('Authorization')
|
||||||
|
response = self.get_json(url, expect_errors=True)
|
||||||
|
self.assertEqual(404, response.status_code)
|
||||||
|
|
||||||
def test_create(self):
|
def test_create(self):
|
||||||
result = self.post_json(self.resource, self.task_01)
|
result = self.post_json(self.resource, self.task_01)
|
||||||
@ -274,6 +301,38 @@ class TestTasksNestedController(base.FunctionalTest):
|
|||||||
|
|
||||||
self.assertEqual(400, response.status_code)
|
self.assertEqual(400, response.status_code)
|
||||||
|
|
||||||
|
def test_tasks_endpoint_privacy(self):
|
||||||
|
self.resource = '/stories/6/tasks'
|
||||||
|
response = self.get_json(self.resource)
|
||||||
|
self.assertEqual(1, len(response))
|
||||||
|
|
||||||
|
# Check that tasks in private stories are correctly filtered
|
||||||
|
headers = {'Authorization': 'Bearer valid_user_token'}
|
||||||
|
response = self.get_json(self.resource, headers=headers)
|
||||||
|
self.assertEqual(0, len(response))
|
||||||
|
self.default_headers.pop('Authorization')
|
||||||
|
response = self.get_json(self.resource)
|
||||||
|
self.assertEqual(0, len(response))
|
||||||
|
|
||||||
|
def test_private_task_visibility(self):
|
||||||
|
url = '/stories/6/tasks/5'
|
||||||
|
# Task with id 5 is in a private story which the user with token
|
||||||
|
# `valid_superuser_token` can see
|
||||||
|
response = self.get_json(url)
|
||||||
|
self.assertEqual('Task in private story', response['title'])
|
||||||
|
|
||||||
|
# The user with token `valid_user_token` can't see the story, and
|
||||||
|
# so shouldn't be able to see the task
|
||||||
|
headers = {'Authorization': 'Bearer valid_user_token'}
|
||||||
|
response = self.get_json(url, headers=headers, expect_errors=True)
|
||||||
|
self.assertEqual(404, response.status_code)
|
||||||
|
|
||||||
|
# Unauthenticated users shouldn't be able to see anything in private
|
||||||
|
# stories
|
||||||
|
self.default_headers.pop('Authorization')
|
||||||
|
response = self.get_json(url, expect_errors=True)
|
||||||
|
self.assertEqual(404, response.status_code)
|
||||||
|
|
||||||
def test_create(self):
|
def test_create(self):
|
||||||
result = self.post_json(self.resource, {
|
result = self.post_json(self.resource, {
|
||||||
'title': 'StoryBoard',
|
'title': 'StoryBoard',
|
||||||
|
@ -18,6 +18,10 @@ from storyboard.tests import base
|
|||||||
|
|
||||||
class TestTimelineEvents(base.FunctionalTest):
|
class TestTimelineEvents(base.FunctionalTest):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestTimelineEvents, self).setUp()
|
||||||
|
self.default_headers['Authorization'] = 'Bearer valid_superuser_token'
|
||||||
|
|
||||||
def test_get_all_events(self):
|
def test_get_all_events(self):
|
||||||
"""Assert that we can retrieve a list of events from a story."""
|
"""Assert that we can retrieve a list of events from a story."""
|
||||||
|
|
||||||
@ -27,6 +31,26 @@ class TestTimelineEvents(base.FunctionalTest):
|
|||||||
self.assertEqual(200, response.status_code)
|
self.assertEqual(200, response.status_code)
|
||||||
self.assertEqual(3, len(response.json))
|
self.assertEqual(3, len(response.json))
|
||||||
|
|
||||||
|
def test_get_all_events_privacy(self):
|
||||||
|
"""Assert that events for private stories are access controlled."""
|
||||||
|
|
||||||
|
url = '/stories/6/events'
|
||||||
|
response = self.get_json(url, expect_errors=True)
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
self.assertEqual(2, len(response.json))
|
||||||
|
|
||||||
|
# The user with token `valid_user_token` can't see the story, and
|
||||||
|
# so shouldn't be able to see the events
|
||||||
|
headers = {'Authorization': 'Bearer valid_user_token'}
|
||||||
|
response = self.get_json(url, headers=headers, expect_errors=True)
|
||||||
|
self.assertEqual(0, len(response.json))
|
||||||
|
|
||||||
|
# Unauthenticated users shouldn't be able to see anything in private
|
||||||
|
# stories
|
||||||
|
self.default_headers.pop('Authorization')
|
||||||
|
response = self.get_json(url, expect_errors=True)
|
||||||
|
self.assertEqual(0, len(response.json))
|
||||||
|
|
||||||
def test_filter_by_event_type(self):
|
def test_filter_by_event_type(self):
|
||||||
"""Assert that we can correctly filter an event by event type."""
|
"""Assert that we can correctly filter an event by event type."""
|
||||||
response = self.get_json('/stories/1/events?event_type=story_created'
|
response = self.get_json('/stories/1/events?event_type=story_created'
|
||||||
|
@ -21,6 +21,7 @@ from storyboard.db.models import AccessToken
|
|||||||
from storyboard.db.models import Branch
|
from storyboard.db.models import Branch
|
||||||
from storyboard.db.models import Comment
|
from storyboard.db.models import Comment
|
||||||
from storyboard.db.models import Milestone
|
from storyboard.db.models import Milestone
|
||||||
|
from storyboard.db.models import Permission
|
||||||
from storyboard.db.models import Project
|
from storyboard.db.models import Project
|
||||||
from storyboard.db.models import ProjectGroup
|
from storyboard.db.models import ProjectGroup
|
||||||
from storyboard.db.models import Story
|
from storyboard.db.models import Story
|
||||||
@ -36,6 +37,7 @@ def load():
|
|||||||
"""Load a batch of useful data into the database that our tests can work
|
"""Load a batch of useful data into the database that our tests can work
|
||||||
with.
|
with.
|
||||||
"""
|
"""
|
||||||
|
session = db.get_session(autocommit=False, in_request=False)
|
||||||
now = datetime.datetime.now(tz=pytz.utc)
|
now = datetime.datetime.now(tz=pytz.utc)
|
||||||
expires_at = now + datetime.timedelta(seconds=3600)
|
expires_at = now + datetime.timedelta(seconds=3600)
|
||||||
expired_at = now + datetime.timedelta(seconds=-3600)
|
expired_at = now + datetime.timedelta(seconds=-3600)
|
||||||
@ -57,7 +59,8 @@ def load():
|
|||||||
openid='otheruser_openid',
|
openid='otheruser_openid',
|
||||||
full_name='Other User',
|
full_name='Other User',
|
||||||
is_superuser=False)
|
is_superuser=False)
|
||||||
])
|
], session)
|
||||||
|
users = session.query(User).all()
|
||||||
|
|
||||||
# Load some preferences for the above users.
|
# Load some preferences for the above users.
|
||||||
load_data([
|
load_data([
|
||||||
@ -86,7 +89,7 @@ def load():
|
|||||||
key='plugin_email_digest',
|
key='plugin_email_digest',
|
||||||
value='False',
|
value='False',
|
||||||
type='bool'),
|
type='bool'),
|
||||||
])
|
], session)
|
||||||
|
|
||||||
# Load a variety of sensibly named access tokens.
|
# Load a variety of sensibly named access tokens.
|
||||||
load_data([
|
load_data([
|
||||||
@ -110,7 +113,7 @@ def load():
|
|||||||
access_token='expired_user_token',
|
access_token='expired_user_token',
|
||||||
expires_in=3600,
|
expires_in=3600,
|
||||||
expires_at=expired_at)
|
expires_at=expired_at)
|
||||||
])
|
], session)
|
||||||
|
|
||||||
# Create some test projects.
|
# Create some test projects.
|
||||||
projects = load_data([
|
projects = load_data([
|
||||||
@ -126,7 +129,7 @@ def load():
|
|||||||
id=3,
|
id=3,
|
||||||
name='tests/project3',
|
name='tests/project3',
|
||||||
description='Project 1 Description - foo')
|
description='Project 1 Description - foo')
|
||||||
])
|
], session)
|
||||||
|
|
||||||
# Create some test project groups.
|
# Create some test project groups.
|
||||||
load_data([
|
load_data([
|
||||||
@ -153,7 +156,17 @@ def load():
|
|||||||
name='projectgroup3',
|
name='projectgroup3',
|
||||||
title='A Sort - foo'
|
title='A Sort - foo'
|
||||||
)
|
)
|
||||||
])
|
], session)
|
||||||
|
|
||||||
|
# Create some permissions
|
||||||
|
load_data([
|
||||||
|
Permission(
|
||||||
|
name='view_story_6',
|
||||||
|
codename='view_story',
|
||||||
|
users=[users[0]]
|
||||||
|
)
|
||||||
|
], session)
|
||||||
|
permissions = session.query(Permission).all()
|
||||||
|
|
||||||
# Create some stories.
|
# Create some stories.
|
||||||
load_data([
|
load_data([
|
||||||
@ -181,8 +194,15 @@ def load():
|
|||||||
id=5,
|
id=5,
|
||||||
title="A Test story 5 - oh hai",
|
title="A Test story 5 - oh hai",
|
||||||
description="Test Description - oh hai"
|
description="Test Description - oh hai"
|
||||||
|
),
|
||||||
|
Story(
|
||||||
|
id=6,
|
||||||
|
title="Test Private Story",
|
||||||
|
description="For Super User's eyes only",
|
||||||
|
private=True,
|
||||||
|
permissions=[permissions[0]]
|
||||||
)
|
)
|
||||||
])
|
], session)
|
||||||
|
|
||||||
# Create some tasks
|
# Create some tasks
|
||||||
load_data([
|
load_data([
|
||||||
@ -229,8 +249,19 @@ def load():
|
|||||||
branch_id=2,
|
branch_id=2,
|
||||||
assignee_id=1,
|
assignee_id=1,
|
||||||
priority='medium'
|
priority='medium'
|
||||||
|
),
|
||||||
|
Task(
|
||||||
|
id=5,
|
||||||
|
creator_id=1,
|
||||||
|
title='Task in private story',
|
||||||
|
status='todo',
|
||||||
|
story_id=6,
|
||||||
|
project_id=2,
|
||||||
|
branch_id=2,
|
||||||
|
assignee_id=1,
|
||||||
|
priority='medium'
|
||||||
)
|
)
|
||||||
])
|
], session)
|
||||||
|
|
||||||
# Generate some timeline events for the above stories.
|
# Generate some timeline events for the above stories.
|
||||||
load_data([
|
load_data([
|
||||||
@ -280,28 +311,48 @@ def load():
|
|||||||
'"old_assignee_id": null, '
|
'"old_assignee_id": null, '
|
||||||
'"task_id": 1, '
|
'"task_id": 1, '
|
||||||
'"new_assignee_id": 2}'
|
'"new_assignee_id": 2}'
|
||||||
|
),
|
||||||
|
TimeLineEvent(
|
||||||
|
id=7,
|
||||||
|
story_id=6,
|
||||||
|
author_id=1,
|
||||||
|
event_type=event.STORY_CREATED,
|
||||||
|
event_info='{"story_id": 6, '
|
||||||
|
'"story_title": "Test Private Story"}'
|
||||||
)
|
)
|
||||||
])
|
], session)
|
||||||
|
|
||||||
# Create a comment.
|
# Create some comments.
|
||||||
load_data([
|
load_data([
|
||||||
Comment(
|
Comment(
|
||||||
id=1,
|
id=1,
|
||||||
content="Test Comment",
|
content="Test Comment",
|
||||||
is_active=True
|
is_active=True
|
||||||
|
),
|
||||||
|
Comment(
|
||||||
|
id=2,
|
||||||
|
content="Comment on a private story",
|
||||||
|
is_active=True
|
||||||
)
|
)
|
||||||
])
|
], session)
|
||||||
|
|
||||||
# Create a timeline event for the above comment.
|
# Create timeline events for the above comments.
|
||||||
load_data([
|
load_data([
|
||||||
TimeLineEvent(
|
TimeLineEvent(
|
||||||
id=7,
|
id=8,
|
||||||
story_id=1,
|
story_id=1,
|
||||||
comment_id=1,
|
comment_id=1,
|
||||||
author_id=1,
|
author_id=1,
|
||||||
event_type=event.USER_COMMENT
|
event_type=event.USER_COMMENT
|
||||||
|
),
|
||||||
|
TimeLineEvent(
|
||||||
|
id=9,
|
||||||
|
story_id=6,
|
||||||
|
comment_id=2,
|
||||||
|
author_id=1,
|
||||||
|
event_type=event.USER_COMMENT
|
||||||
)
|
)
|
||||||
])
|
], session)
|
||||||
|
|
||||||
# Load some subscriptions.
|
# Load some subscriptions.
|
||||||
load_data([
|
load_data([
|
||||||
@ -323,7 +374,7 @@ def load():
|
|||||||
target_type='story',
|
target_type='story',
|
||||||
target_id=1
|
target_id=1
|
||||||
),
|
),
|
||||||
])
|
], session)
|
||||||
|
|
||||||
# Load some branches
|
# Load some branches
|
||||||
load_data([
|
load_data([
|
||||||
@ -345,7 +396,7 @@ def load():
|
|||||||
name='master',
|
name='master',
|
||||||
restricted=True
|
restricted=True
|
||||||
)
|
)
|
||||||
])
|
], session)
|
||||||
|
|
||||||
# Load some milestones
|
# Load some milestones
|
||||||
load_data([
|
load_data([
|
||||||
@ -359,27 +410,30 @@ def load():
|
|||||||
name='test_milestone_02',
|
name='test_milestone_02',
|
||||||
branch_id=2
|
branch_id=2
|
||||||
)
|
)
|
||||||
])
|
], session)
|
||||||
|
|
||||||
# Load some teams
|
# Load some teams
|
||||||
load_data([
|
load_data([
|
||||||
Team(
|
Team(
|
||||||
id=1,
|
id=1,
|
||||||
name='test_team_1'
|
name='test_team_1',
|
||||||
|
users=[users[0]]
|
||||||
),
|
),
|
||||||
Team(
|
Team(
|
||||||
id=2,
|
id=2,
|
||||||
name='test_team_2'
|
name='test_team_2',
|
||||||
|
users=users[1:]
|
||||||
)
|
)
|
||||||
])
|
], session)
|
||||||
|
|
||||||
|
|
||||||
def load_data(data):
|
def load_data(data, session=None):
|
||||||
"""Pre load test data into the database.
|
"""Pre load test data into the database.
|
||||||
|
|
||||||
:param data An iterable collection of database models.
|
:param data An iterable collection of database models.
|
||||||
"""
|
"""
|
||||||
session = db.get_session(autocommit=False, in_request=False)
|
if session is None:
|
||||||
|
session = db.get_session(autocommit=False, in_request=False)
|
||||||
|
|
||||||
for entity in data:
|
for entity in data:
|
||||||
session.add(entity)
|
session.add(entity)
|
||||||
|
Loading…
Reference in New Issue
Block a user