Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tests for private search [OSF-6898] #6184

Closed
wants to merge 65 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
7bdb917
Shuffle classes around in test_search/test_views
chadwhitacre Aug 23, 2016
4d6d0d7
Start a class for project search API (v1) tests
chadwhitacre Aug 23, 2016
b3d31cc
Rewrite tests using parameterization
chadwhitacre Aug 29, 2016
aae954d
Expand to registrations
chadwhitacre Aug 29, 2016
c579dfa
Expand to components
chadwhitacre Aug 30, 2016
626eaee
Clean up tests we have so far
chadwhitacre Aug 30, 2016
82f0054
Expand to files
chadwhitacre Aug 30, 2016
a0dedae
Use mock_archive for the registration case
chadwhitacre Sep 5, 2016
ca09ac6
Fix two public/anon tests
chadwhitacre Sep 13, 2016
a0ddd4b
There is no such thing as a "private registration"
chadwhitacre Sep 16, 2016
6c4eaf5
Extend to cover authenticated users
chadwhitacre Sep 16, 2016
1f3c16e
Factor out factory functions
chadwhitacre Sep 16, 2016
adebf15
Naively failing tests for private read
chadwhitacre Sep 19, 2016
8e2e87e
Start stubbing out fixture tests
chadwhitacre Sep 19, 2016
448386a
Write tests for project creation
chadwhitacre Sep 19, 2016
8a13e46
More tests for projects, registrations, components
chadwhitacre Sep 19, 2016
ab07f2c
Additional tests for files
chadwhitacre Sep 19, 2016
e75452f
Check for default permissions
chadwhitacre Sep 19, 2016
c33663f
Don't return from makers
chadwhitacre Sep 19, 2016
bacab80
Wire up a contributor: *now* they're failing tests
chadwhitacre Sep 23, 2016
7260be1
Remove some tests that don't matter anymore
chadwhitacre Sep 26, 2016
7631317
Remove more out of date tests
chadwhitacre Sep 26, 2016
37b41b9
The thing is a node
chadwhitacre Sep 27, 2016
9367f1a
Fix TestMakers
chadwhitacre Sep 27, 2016
2f3bd8a
Start evolving towards a generator
chadwhitacre Sep 27, 2016
68ff540
Generate test cases programmatically
chadwhitacre Sep 27, 2016
59f9fab
Add a couple tests for the generator itself
chadwhitacre Sep 27, 2016
2c5bb2e
Push perms work out of test func
chadwhitacre Sep 27, 2016
28ca96b
Add a test suite for the permfuncs
chadwhitacre Sep 27, 2016
c0bafac
Factor out varyfuncs and nodefuncs
chadwhitacre Sep 28, 2016
58f12a3
Move permissions tests to their own file
chadwhitacre Sep 28, 2016
1067bf0
Teach parameterized tests about expected failures
chadwhitacre Oct 3, 2016
53c0a82
Fix bug with unexpected successes
chadwhitacre Oct 5, 2016
7856c5a
Tweak some names
chadwhitacre Oct 5, 2016
b93af5f
Add in unapproved unembargoed registrations
chadwhitacre Oct 6, 2016
c697edc
Deduplicate a couple things
chadwhitacre Oct 7, 2016
41435f4
Neaten up varyfuncs
chadwhitacre Oct 7, 2016
b71cb55
Embargoed, unapproved registration
chadwhitacre Oct 10, 2016
3b75f26
Start generating reg. variants programmatically
chadwhitacre Oct 15, 2016
2b64ea4
Bring back approved embargo
chadwhitacre Oct 15, 2016
5ec0c2d
Mix completeness into the regfunc generator
chadwhitacre Oct 31, 2016
8691b0a
Fill out the rest of the new regfunc tests
chadwhitacre Oct 31, 2016
6fca079
Add a test for the number of tests
chadwhitacre Nov 8, 2016
64d0eb0
Include retraction info in regfunc name
chadwhitacre Nov 8, 2016
1388a39
Mix in unapproved retractions
chadwhitacre Nov 8, 2016
dff447a
DRY up the regfunc tests
chadwhitacre Nov 8, 2016
dcc5afc
Remove redundant check from some tests
chadwhitacre Nov 8, 2016
a00d846
Round out regfuncs with approved retractions
chadwhitacre Nov 9, 2016
415cf42
Update expectations regarding retractions
chadwhitacre Nov 19, 2016
8bca5a9
Update number of tests
chadwhitacre Nov 21, 2016
6ff3658
Remove unused constants
chadwhitacre Nov 21, 2016
5b1efc3
Remove a vestigial helper
chadwhitacre Nov 21, 2016
96a1a46
Refactor into multiple files
chadwhitacre Nov 21, 2016
056b5a8
Clean up some names and comments
chadwhitacre Nov 23, 2016
78de3ab
Fix the build and clean up a few more things
chadwhitacre Nov 24, 2016
b09234a
Add tests for searching wiki content
chadwhitacre Nov 26, 2016
4d956fd
Rewire nodefuncs ahead of varying parent.is_public
chadwhitacre Nov 26, 2016
188b42f
Add in cases where parent differs from component
chadwhitacre Nov 26, 2016
dc77f5a
Make room for previously embargoed registrations
chadwhitacre Nov 27, 2016
2a79ef7
Refactor for clarity
chadwhitacre Dec 7, 2016
c89f932
Refactor varyfuncs a bit
chadwhitacre Dec 7, 2016
da489a2
Add in formerly embargoed cases
chadwhitacre Dec 7, 2016
624575b
DRY up regfunc tests
chadwhitacre Dec 9, 2016
6e2510f
Use a dict other than locals()
chadwhitacre Dec 9, 2016
c5e5800
Make unembargoed parallel to unretracted
chadwhitacre Dec 9, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 117 additions & 0 deletions tests/test_search/test_permissions/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# -*- coding: utf-8 -*-
"""This is a test suite for permissions on the search_search endpoint. It has four parts:

- nodefuncs - functions that return Nodes
- permfuncs - functions that set permissions on a Node
- varyfuncs - functions that vary the (non-permission) state of a Node
- TestSearchSearchAPI - the actual tests against the search_search API,
which are generated from the combinations of the above three function types

"""
from __future__ import absolute_import, division, print_function, unicode_literals

import sys
import unittest
import unittest.case

from nose.tools import assert_equal

from nose_parameterized import parameterized
from tests.test_search import SearchTestCase
from tests.test_search.test_permissions.test_varyfuncs import VARYFUNCS, REGFUNCS, REGFUNCS_PRIVATE
from tests.test_search.test_permissions.test_varyfuncs import base
from tests.test_search.test_permissions.test_permfuncs import anon, auth, read
from tests.test_search.test_permissions.test_nodefuncs import NODEFUNCS, NODEFUNCS_PRIVATE
from website.project.model import Node
from website.util import api_url_for


def determine_case_name(nodefunc, permfunc, varyfunc, should_see, **_):
return "{}{}_{}_{}".format(
'' if varyfunc is base else varyfunc.__name__ + '_',
nodefunc.__name__,
'is_shown_to' if should_see else 'is_hidden_from',
permfunc.__name__
)


def is_private(nodefunc, varyfunc):
return nodefunc in NODEFUNCS_PRIVATE or varyfunc in REGFUNCS_PRIVATE


def want(name):
# filter cases here since we can't use nose's usual mechanisms with parameterization
return True


def generate_cases():
for nodefunc in NODEFUNCS:
for permfunc in (anon, auth, read):
for varyfunc in VARYFUNCS:
if nodefunc in NODEFUNCS_PRIVATE and varyfunc in REGFUNCS:
# Registration makes a node public, so skip it.
continue
should_see = permfunc is read if is_private(nodefunc, varyfunc) else True
name = determine_case_name(**locals())
if want(name):
yield name, nodefunc, permfunc, varyfunc, should_see


class TestGenerateCases(unittest.TestCase):

# gc - generate_cases

def test_gc_generates_cases(self):
assert_equal(len(list(generate_cases())), 342)

def test_gc_doesnt_create_any_nodes(self):
list(generate_cases())
assert_equal(len(Node.find()), 0)


def possiblyExpectFailure(case):

# This is a hack to conditionally wrap a failure expectation around *some*
# of the cases we're feeding to node-parameterized. TODO It can be removed
# when we write the code to unfail the tests.

def test(*a, **kw): # name must start with test or it's ignored
_, _, nodefunc, permfunc, varyfunc, _ = a

# The tests we expect to fail are those where a node is in a private
# state, and a user does have read permission: a private search, in
# other words.

if is_private(nodefunc, varyfunc) and permfunc is read:

# This bit is copied from the unittest/case.py:expectedFailure
# decorator.

try:
case(*a, **kw)
except Exception:
raise unittest.case._ExpectedFailure(sys.exc_info())
raise unittest.case._UnexpectedSuccess
else:
case(*a, **kw)
return test


class TestSearchSearchAPI(SearchTestCase):
"""Exercises the website.search.views.search_search view.
"""

def search(self, query, category, auth):
url = api_url_for('search_search')
data = {'q': 'category:{} AND {}'.format(category, query)}
return self.app.get(url, data, auth=auth).json['results']

@parameterized.expand(generate_cases)
@possiblyExpectFailure
def test(self, ignored, nodefunc, permfunc, varyfunc, should_see):
node = nodefunc()
auth = permfunc(node)
query, type_, key, expected_name = varyfunc(node)
expected = [(expected_name, type_)] if should_see else []
results = self.search(query, type_, auth)
assert_equal([(x[key], x['category']) for x in results], expected)
93 changes: 93 additions & 0 deletions tests/test_search/test_permissions/test_nodefuncs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, print_function, unicode_literals

from nose.tools import assert_equal, ok_

from modularodm import Q

from tests import factories
from tests.base import DbIsolationMixin
from tests.test_search import OsfTestCase
from website.project.model import Node


def _project(is_public):
project = factories.ProjectFactory(title='Flim Flammity', is_public=is_public)
project.update_search()
return project

def public_project(): return _project(True)
def private_project(): return _project(False)


def _component(is_public, parent_is_public):
project = factories.ProjectFactory(title='Slim Slammity', is_public=parent_is_public)
project.update_search()
component = factories.NodeFactory(
title='Flim Flammity',
parent=project,
is_public=is_public,
)
component.update_search()
return component

def public_component_of_a_public_project(): return _component(True, True)
def public_component_of_a_private_project(): return _component(True, False)
def private_component_of_a_public_project(): return _component(False, True)
def private_component_of_a_private_project(): return _component(False, False)


NODEFUNCS_PRIVATE = [
private_project,
private_component_of_a_public_project,
private_component_of_a_private_project
]
NODEFUNCS = [
public_project,
public_component_of_a_public_project,
public_component_of_a_private_project
] + NODEFUNCS_PRIVATE


class TestNodeFuncs(DbIsolationMixin, OsfTestCase):

def test_there_are_no_nodes_to_start_with(self):
assert_equal(Node.find().count(), 0)


# pp - {public,private}_project

def test_pp_makes_public_project_public(self):
public_project()
ok_(Node.find_one().is_public)

def test_pp_makes_private_project_private(self):
private_project()
ok_(not Node.find_one().is_public)


# pcoapp - {public,private}_component_of_a_{public,private}_project

def test_pcoapp_makes_public_component_of_a_public_project(self):
public_component_of_a_public_project()
component = Node.find_one(Q('parent_node', 'ne', None))
ok_(component.is_public)
ok_(component.parent_node.is_public)

def test_pcoapp_makes_public_component_of_a_private_project(self):
public_component_of_a_private_project()
component = Node.find_one(Q('parent_node', 'ne', None))
ok_(component.is_public)
ok_(not component.parent_node.is_public)

def test_pcoapp_makes_private_component_of_a_public_project(self):
private_component_of_a_public_project()
component = Node.find_one(Q('parent_node', 'ne', None))
ok_(not component.is_public)
ok_(component.parent_node.is_public)

def test_pcoapp_makes_private_component_of_a_private_project(self):
private_component_of_a_private_project()
component = Node.find_one(Q('parent_node', 'ne', None))
ok_(not component.is_public)
ok_(not component.parent_node.is_public)
73 changes: 73 additions & 0 deletions tests/test_search/test_permissions/test_permfuncs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, print_function, unicode_literals

from nose.tools import assert_equal, assert_in, assert_not_in

from modularodm import Q

from framework.auth.core import User
from tests import factories
from tests.base import DbIsolationMixin
from tests.test_search import OsfTestCase
from tests.test_search.test_permissions.test_nodefuncs import public_project
from website.util import permissions
from website.project.model import Node


def anon(node):
return None


def auth(node):
return factories.AuthUserFactory().auth


def read(node):
user = factories.AuthUserFactory()
node.add_contributor(user, permissions.READ)
return user.auth


class TestPermFuncs(DbIsolationMixin, OsfTestCase):

@staticmethod
def get_user_id_from_authtuple(authtuple):
return User.find_one(Q('emails', 'eq', authtuple[0]))._id


# anon

def test_anon_returns_none(self):
assert_equal(anon(public_project()), None)

def test_anon_makes_no_user(self):
anon(public_project())
assert_equal(len(User.find()), 1) # only the project creator


# auth

def test_auth_returns_authtuple(self):
assert_equal(auth(public_project())[1], 'password')

def test_auth_creates_a_user(self):
auth(public_project())
assert_equal(len(User.find()), 2) # project creator + 1

def test_auth_user_is_not_a_contributor_on_the_node(self):
user_id = self.get_user_id_from_authtuple(auth(public_project()))
assert_not_in(user_id, Node.find_one().permissions.keys())


# read

def test_read_returns_authtuple(self):
assert_equal(read(public_project())[1], 'password')

def test_read_creates_a_user(self):
read(public_project())
assert_equal(len(User.find()), 2) # project creator + 1

def test_read_user_is_a_contributor_on_the_node(self):
user_id = self.get_user_id_from_authtuple(read(public_project()))
assert_in(user_id, Node.find_one().permissions.keys())
Loading