Add some tests for checking private story behaviour
This adds some tests which verify the behaviour of private stories is as intended, and that they are correctly filtered out of any related results such as tasks and comments. Some changes were necessary in mock_data.py to account for this. The session used to insert data is now shared for all the test objects, rather than creating a new session per object type. This makes it easier to query the created data to populate the various many-to-many relationships in the StoryBoard data model, to allow for testing of things which require permissions to be created and populated. Change-Id: Ib4db5b3d07363d3bdc97ad825a12f8882a0aa8f1
This commit is contained in:
parent
6cdc6f4bfb
commit
0207e1704b
@ -36,6 +36,24 @@ class TestComments(base.FunctionalTest):
|
||||
response = self.get_json(self.comments_resource % self.story_id)
|
||||
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):
|
||||
self.post_json(self.comments_resource % self.story_id, self.comment_01)
|
||||
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):
|
||||
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):
|
||||
response = self.post_json(self.resource, self.story_01)
|
||||
@ -62,22 +84,22 @@ class TestStories(base.FunctionalTest):
|
||||
self.assertEqual(story['story_type_id'],
|
||||
created_story['story_type_id'])
|
||||
|
||||
@unittest.skip("vulnerabilities are not supported.")
|
||||
def test_create_private_vulnerability(self):
|
||||
def test_create_private_story(self):
|
||||
story = {
|
||||
'title': 'StoryBoard',
|
||||
'description': 'Awesome Task Tracker',
|
||||
'story_type_id': 3
|
||||
'private': True,
|
||||
'users': [{'id': 1}]
|
||||
}
|
||||
response = self.post_json(self.resource, story)
|
||||
created_story = response.json
|
||||
|
||||
self.assertEqual(story['title'], created_story['title'])
|
||||
self.assertEqual(story['description'], created_story['description'])
|
||||
self.assertEqual(story['story_type_id'],
|
||||
created_story['story_type_id'])
|
||||
self.assertEqual(story['private'],
|
||||
created_story['private'])
|
||||
|
||||
@unittest.skip("vulnerabilities are not supported.")
|
||||
@unittest.skip("public vulnerabilities are not supported.")
|
||||
def test_create_public_vulnerability(self):
|
||||
story = {
|
||||
'title': 'StoryBoard',
|
||||
@ -129,25 +151,31 @@ class TestStories(base.FunctionalTest):
|
||||
{'story_type_id': 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_vulnerability(self):
|
||||
def test_update_private_to_public(self):
|
||||
story = {
|
||||
'title': 'StoryBoard',
|
||||
'description': 'Awesome Task Tracker',
|
||||
'story_type_id': 3
|
||||
'private': True
|
||||
}
|
||||
|
||||
response = self.post_json(self.resource, story)
|
||||
created_story = response.json
|
||||
|
||||
self.assertEqual(story["story_type_id"],
|
||||
created_story["story_type_id"])
|
||||
self.assertEqual(story['private'],
|
||||
created_story['private'])
|
||||
|
||||
response = self.put_json(self.resource +
|
||||
('/%s' % created_story["id"]),
|
||||
{'story_type_id': 4})
|
||||
created_story = response.json
|
||||
self.assertEqual(4, created_story['story_type_id'])
|
||||
('/%s' % created_story['id']),
|
||||
{'private': False})
|
||||
updated_story = response.json
|
||||
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):
|
||||
response = self.put_json(self.resource + '/1', {'story_type_id': 2},
|
||||
|
@ -107,7 +107,34 @@ class TestTasksPrimary(base.FunctionalTest):
|
||||
|
||||
def test_tasks_endpoint(self):
|
||||
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.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):
|
||||
result = self.post_json(self.resource, self.task_01)
|
||||
@ -274,6 +301,38 @@ class TestTasksNestedController(base.FunctionalTest):
|
||||
|
||||
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):
|
||||
result = self.post_json(self.resource, {
|
||||
'title': 'StoryBoard',
|
||||
|
@ -18,6 +18,10 @@ from storyboard.tests import base
|
||||
|
||||
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):
|
||||
"""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(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):
|
||||
"""Assert that we can correctly filter an event by event type."""
|
||||
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 Comment
|
||||
from storyboard.db.models import Milestone
|
||||
from storyboard.db.models import Permission
|
||||
from storyboard.db.models import Project
|
||||
from storyboard.db.models import ProjectGroup
|
||||
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
|
||||
with.
|
||||
"""
|
||||
session = db.get_session(autocommit=False, in_request=False)
|
||||
now = datetime.datetime.now(tz=pytz.utc)
|
||||
expires_at = now + datetime.timedelta(seconds=3600)
|
||||
expired_at = now + datetime.timedelta(seconds=-3600)
|
||||
@ -57,7 +59,8 @@ def load():
|
||||
openid='otheruser_openid',
|
||||
full_name='Other User',
|
||||
is_superuser=False)
|
||||
])
|
||||
], session)
|
||||
users = session.query(User).all()
|
||||
|
||||
# Load some preferences for the above users.
|
||||
load_data([
|
||||
@ -86,7 +89,7 @@ def load():
|
||||
key='plugin_email_digest',
|
||||
value='False',
|
||||
type='bool'),
|
||||
])
|
||||
], session)
|
||||
|
||||
# Load a variety of sensibly named access tokens.
|
||||
load_data([
|
||||
@ -110,7 +113,7 @@ def load():
|
||||
access_token='expired_user_token',
|
||||
expires_in=3600,
|
||||
expires_at=expired_at)
|
||||
])
|
||||
], session)
|
||||
|
||||
# Create some test projects.
|
||||
projects = load_data([
|
||||
@ -126,7 +129,7 @@ def load():
|
||||
id=3,
|
||||
name='tests/project3',
|
||||
description='Project 1 Description - foo')
|
||||
])
|
||||
], session)
|
||||
|
||||
# Create some test project groups.
|
||||
load_data([
|
||||
@ -153,7 +156,17 @@ def load():
|
||||
name='projectgroup3',
|
||||
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.
|
||||
load_data([
|
||||
@ -181,8 +194,15 @@ def load():
|
||||
id=5,
|
||||
title="A Test story 5 - 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
|
||||
load_data([
|
||||
@ -229,8 +249,19 @@ def load():
|
||||
branch_id=2,
|
||||
assignee_id=1,
|
||||
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.
|
||||
load_data([
|
||||
@ -280,28 +311,48 @@ def load():
|
||||
'"old_assignee_id": null, '
|
||||
'"task_id": 1, '
|
||||
'"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([
|
||||
Comment(
|
||||
id=1,
|
||||
content="Test Comment",
|
||||
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([
|
||||
TimeLineEvent(
|
||||
id=7,
|
||||
id=8,
|
||||
story_id=1,
|
||||
comment_id=1,
|
||||
author_id=1,
|
||||
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_data([
|
||||
@ -323,7 +374,7 @@ def load():
|
||||
target_type='story',
|
||||
target_id=1
|
||||
),
|
||||
])
|
||||
], session)
|
||||
|
||||
# Load some branches
|
||||
load_data([
|
||||
@ -345,7 +396,7 @@ def load():
|
||||
name='master',
|
||||
restricted=True
|
||||
)
|
||||
])
|
||||
], session)
|
||||
|
||||
# Load some milestones
|
||||
load_data([
|
||||
@ -359,27 +410,30 @@ def load():
|
||||
name='test_milestone_02',
|
||||
branch_id=2
|
||||
)
|
||||
])
|
||||
], session)
|
||||
|
||||
# Load some teams
|
||||
load_data([
|
||||
Team(
|
||||
id=1,
|
||||
name='test_team_1'
|
||||
name='test_team_1',
|
||||
users=[users[0]]
|
||||
),
|
||||
Team(
|
||||
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.
|
||||
|
||||
: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:
|
||||
session.add(entity)
|
||||
|
Loading…
x
Reference in New Issue
Block a user