mirror of
https://github.com/ansible/awx.git
synced 2024-11-02 01:21:21 +03:00
commit
11443eb22d
65
awx/main/tests/factories/README.md
Normal file
65
awx/main/tests/factories/README.md
Normal file
@ -0,0 +1,65 @@
|
||||
factories
|
||||
=========
|
||||
|
||||
This is a module for defining stand-alone factories and fixtures. Ideally a fixture will implement a single item.
|
||||
DO NOT decorate fixtures in this module with the @pytest.fixture. These fixtures are to be combined
|
||||
with fixture factories and composition using the `conftest.py` convention. Those composed fixtures
|
||||
will be decorated for usage and discovery.
|
||||
|
||||
Use the fixtures directly in factory methods to build up the desired set of components and relationships.
|
||||
Each fixture should create exactly one object and should support the option for that object to be persisted
|
||||
or not.
|
||||
|
||||
A factory should create at a minimum a single object for that factory type. The creation of any
|
||||
associated objects should be explicit. For example, the `create_organization` factory when given only
|
||||
a `name` parameter will create an Organization but it will not implicitly create any other objects.
|
||||
|
||||
teams
|
||||
-----
|
||||
|
||||
There is some special handling for users when adding teams. There is a short hand that allows you to
|
||||
assign a user to the member\_role of a team using the string notation of `team_name:user_name`. There is
|
||||
no shortcut for adding a user to the admin\_role of a team. See the roles section for more information
|
||||
about how to do that.
|
||||
|
||||
roles
|
||||
-----
|
||||
|
||||
The roles helper allows you pass in roles to a factory. These roles assignments will happen after
|
||||
the objects are created. Using the roles parameter required that persisted=True (default).
|
||||
|
||||
You can use a string notation of `object_name.role_name:user` OR `object_name.role_name:object_name.child_role`
|
||||
|
||||
obj.parent_role:user # This will make the user a member of parent_role
|
||||
obj1.role:obj2.role # This will make obj2 a child role of obj1
|
||||
|
||||
team1.admin_role:joe
|
||||
team1.admin_role:project1.admin_role
|
||||
|
||||
examples
|
||||
--------
|
||||
|
||||
objects = create_organization('test-org')
|
||||
assert objects.organization.name == 'test-org'
|
||||
|
||||
objects = create_organization('test-org', projects=['test-proj'])
|
||||
assert objects.projects.test-proj.organization == objects.organization
|
||||
|
||||
objects = create_organization('test-org', persisted=False)
|
||||
assert not objects.organization.pk
|
||||
|
||||
patterns
|
||||
--------
|
||||
|
||||
`mk` functions are single object fixtures. They should create only a single object with the minimum deps.
|
||||
They should also accept a `persited` flag, if they must be persisted to work, they raise an error if persisted=False
|
||||
|
||||
`generate` and `apply` functions are helpers that build up the various parts of a `create` functions objects. These
|
||||
should be useful for more than one create function to use and should explicitly accept all of the values needed
|
||||
to execute. These functions should also be robust and have very speciifc error reporting about constraints and/or
|
||||
bad values.
|
||||
|
||||
`create` functions compose many of the `mk` and `generate` functions to make different object
|
||||
factories. These functions when giving the minimum set of arguments should only produce a
|
||||
single artifact (or the minimum needed for that object). These should be wrapped by discoverable
|
||||
fixtures in various conftest.py files.
|
16
awx/main/tests/factories/__init__.py
Normal file
16
awx/main/tests/factories/__init__.py
Normal file
@ -0,0 +1,16 @@
|
||||
from .tower import (
|
||||
create_organization,
|
||||
create_job_template,
|
||||
create_notification_template,
|
||||
)
|
||||
|
||||
from .exc import (
|
||||
NotUnique,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
'create_organization',
|
||||
'create_job_template',
|
||||
'create_notification_template',
|
||||
'NotUnique',
|
||||
]
|
5
awx/main/tests/factories/exc.py
Normal file
5
awx/main/tests/factories/exc.py
Normal file
@ -0,0 +1,5 @@
|
||||
class NotUnique(Exception):
|
||||
def __init__(self, name, objects):
|
||||
msg = '{} is not a unique key, found {}={}'.format(name, name, objects[name])
|
||||
super(Exception, self).__init__(msg)
|
||||
|
124
awx/main/tests/factories/fixtures.py
Normal file
124
awx/main/tests/factories/fixtures.py
Normal file
@ -0,0 +1,124 @@
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from awx.main.models import (
|
||||
Organization,
|
||||
Project,
|
||||
Team,
|
||||
Instance,
|
||||
JobTemplate,
|
||||
NotificationTemplate,
|
||||
Credential,
|
||||
Inventory,
|
||||
Label,
|
||||
)
|
||||
|
||||
# mk methods should create only a single object of a single type.
|
||||
# they should also have the option of being persisted or not.
|
||||
# if the object must be persisted an error should be raised when
|
||||
# persisted=False
|
||||
#
|
||||
|
||||
def mk_instance(persisted=True):
|
||||
if not persisted:
|
||||
raise RuntimeError('creating an Instance requires persisted=True')
|
||||
from django.conf import settings
|
||||
return Instance.objects.get_or_create(uuid=settings.SYSTEM_UUID, primary=True, hostname="instance.example.org")
|
||||
|
||||
|
||||
def mk_organization(name, description=None, persisted=True):
|
||||
description = description or '{}-description'.format(name)
|
||||
org = Organization(name=name, description=description)
|
||||
if persisted:
|
||||
mk_instance(persisted)
|
||||
org.save()
|
||||
return org
|
||||
|
||||
|
||||
def mk_label(name, organization=None, description=None, persisted=True):
|
||||
description = description or '{}-description'.format(name)
|
||||
label = Label(name=name, description=description)
|
||||
if organization is not None:
|
||||
label.organization = organization
|
||||
if persisted:
|
||||
label.save()
|
||||
return label
|
||||
|
||||
|
||||
def mk_team(name, organization=None, persisted=True):
|
||||
team = Team(name=name)
|
||||
if organization is not None:
|
||||
team.organization = organization
|
||||
if persisted:
|
||||
mk_instance(persisted)
|
||||
team.save()
|
||||
return team
|
||||
|
||||
|
||||
def mk_user(name, is_superuser=False, organization=None, team=None, persisted=True):
|
||||
user = User(username=name, is_superuser=is_superuser)
|
||||
if persisted:
|
||||
user.save()
|
||||
if organization is not None:
|
||||
organization.member_role.members.add(user)
|
||||
if team is not None:
|
||||
team.member_role.members.add(user)
|
||||
return user
|
||||
|
||||
|
||||
def mk_project(name, organization=None, description=None, persisted=True):
|
||||
description = description or '{}-description'.format(name)
|
||||
project = Project(name=name, description=description)
|
||||
if organization is not None:
|
||||
project.organization = organization
|
||||
if persisted:
|
||||
project.save()
|
||||
return project
|
||||
|
||||
|
||||
def mk_credential(name, cloud=False, kind='ssh', persisted=True):
|
||||
cred = Credential(name=name, cloud=cloud, kind=kind)
|
||||
if persisted:
|
||||
cred.save()
|
||||
return cred
|
||||
|
||||
|
||||
def mk_notification_template(name, notification_type='webhook', configuration=None, organization=None, persisted=True):
|
||||
nt = NotificationTemplate(name=name)
|
||||
nt.notification_type = notification_type
|
||||
nt.notification_configuration = configuration or dict(url="http://localhost", headers={"Test": "Header"})
|
||||
|
||||
if organization is not None:
|
||||
nt.organization = organization
|
||||
if persisted:
|
||||
nt.save()
|
||||
return nt
|
||||
|
||||
|
||||
def mk_inventory(name, organization=None, persisted=True):
|
||||
inv = Inventory(name=name)
|
||||
if organization is not None:
|
||||
inv.organization = organization
|
||||
if persisted:
|
||||
inv.save()
|
||||
return inv
|
||||
|
||||
|
||||
def mk_job_template(name, job_type='run',
|
||||
organization=None, inventory=None,
|
||||
credential=None, persisted=True,
|
||||
project=None):
|
||||
jt = JobTemplate(name=name, job_type=job_type)
|
||||
|
||||
jt.inventory = inventory
|
||||
if jt.inventory is None:
|
||||
jt.ask_inventory_on_launch = True
|
||||
|
||||
jt.credential = credential
|
||||
if jt.credential is None:
|
||||
jt.ask_credential_on_launch = True
|
||||
|
||||
jt.project = project
|
||||
|
||||
if persisted:
|
||||
jt.save()
|
||||
return jt
|
280
awx/main/tests/factories/tower.py
Normal file
280
awx/main/tests/factories/tower.py
Normal file
@ -0,0 +1,280 @@
|
||||
from collections import namedtuple
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from awx.main.models import (
|
||||
Organization,
|
||||
Project,
|
||||
Team,
|
||||
NotificationTemplate,
|
||||
Credential,
|
||||
Inventory,
|
||||
Label,
|
||||
)
|
||||
|
||||
from .fixtures import (
|
||||
mk_organization,
|
||||
mk_team,
|
||||
mk_user,
|
||||
mk_job_template,
|
||||
mk_credential,
|
||||
mk_inventory,
|
||||
mk_project,
|
||||
mk_label,
|
||||
mk_notification_template,
|
||||
)
|
||||
|
||||
from .exc import NotUnique
|
||||
|
||||
|
||||
def generate_role_objects(objects):
|
||||
'''generate_role_objects assembles a dictionary of all possible objects by name.
|
||||
It will raise an exception if any of the objects share a name due to the fact that
|
||||
it is to be used with apply_roles, which expects unique object names.
|
||||
|
||||
roles share a common name e.g. admin_role, member_role. This ensures that the
|
||||
roles short hand used for mapping Roles and Users in apply_roles will function as desired.
|
||||
'''
|
||||
combined_objects = {}
|
||||
for o in objects:
|
||||
if type(o) is dict:
|
||||
for k,v in o.iteritems():
|
||||
if combined_objects.get(k) is not None:
|
||||
raise NotUnique(k, combined_objects)
|
||||
combined_objects[k] = v
|
||||
elif hasattr(o, 'name'):
|
||||
if combined_objects.get(o.name) is not None:
|
||||
raise NotUnique(o.name, combined_objects)
|
||||
combined_objects[o.name] = o
|
||||
else:
|
||||
if o is not None:
|
||||
raise RuntimeError('expected a list of dict or list of list, got a type {}'.format(type(o)))
|
||||
return combined_objects
|
||||
|
||||
def apply_roles(roles, objects, persisted):
|
||||
'''apply_roles evaluates a list of Role relationships represented as strings.
|
||||
The format of this string is 'role:[user|role]'. When a user is provided, they will be
|
||||
made a member of the role on the LHS. When a role is provided that role will be added to
|
||||
the children of the role on the LHS.
|
||||
|
||||
This function assumes that objects is a dictionary that contains a unique set of key to value
|
||||
mappings for all possible "Role objects". See the example below:
|
||||
|
||||
Mapping Users
|
||||
-------------
|
||||
roles = ['org1.admin_role:user1', 'team1.admin_role:user1']
|
||||
objects = {'org1': Organization, 'team1': Team, 'user1': User]
|
||||
|
||||
Mapping Roles
|
||||
-------------
|
||||
roles = ['org1.admin_role:team1.admin_role']
|
||||
objects = {'org1': Organization, 'team1': Team}
|
||||
|
||||
Invalid Mapping
|
||||
---------------
|
||||
roles = ['org1.admin_role:team1.admin_role']
|
||||
objects = {'org1': Organization', 'user1': User} # Exception, no team1 entry
|
||||
'''
|
||||
if roles is None:
|
||||
return None
|
||||
|
||||
if not persisted:
|
||||
raise RuntimeError('roles can not be used when persisted=False')
|
||||
|
||||
for role in roles:
|
||||
obj_role, sep, member_role = role.partition(':')
|
||||
if not member_role:
|
||||
raise RuntimeError('you must provide an assignment role, got None')
|
||||
|
||||
obj_str, o_role_str = obj_role.split('.')
|
||||
member_str, m_sep, m_role_str = member_role.partition('.')
|
||||
|
||||
obj = objects[obj_str]
|
||||
obj_role = getattr(obj, o_role_str)
|
||||
|
||||
member = objects[member_str]
|
||||
if m_role_str:
|
||||
if hasattr(member, m_role_str):
|
||||
member_role = getattr(member, m_role_str)
|
||||
obj_role.children.add(member_role)
|
||||
else:
|
||||
raise RuntimeError('unable to find {} role for {}'.format(m_role_str, member_str))
|
||||
else:
|
||||
if type(member) is User:
|
||||
obj_role.members.add(member)
|
||||
else:
|
||||
raise RuntimeError('unable to add non-user {} for members list of {}'.format(member_str, obj_str))
|
||||
|
||||
def generate_users(organization, teams, superuser, persisted, **kwargs):
|
||||
'''generate_users evaluates a mixed list of User objects and strings.
|
||||
If a string is encountered a user with that username is created and added to the lookup dict.
|
||||
If a User object is encountered the User.username is used as a key for the lookup dict.
|
||||
|
||||
A short hand for assigning a user to a team is available in the following format: "team_name:username".
|
||||
If a string in that format is encounted an attempt to lookup the team by the key team_name from the teams
|
||||
argumnent is made, a KeyError will be thrown if the team does not exist in the dict. The teams argument should
|
||||
be a dict of {Team.name:Team}
|
||||
'''
|
||||
users = {}
|
||||
key = 'superusers' if superuser else 'users'
|
||||
if key in kwargs and kwargs.get(key) is not None:
|
||||
for u in kwargs[key]:
|
||||
if type(u) is User:
|
||||
users[u.username] = u
|
||||
else:
|
||||
p1, sep, p2 = u.partition(':')
|
||||
if p2:
|
||||
t = teams[p1]
|
||||
users[p2] = mk_user(p2, organization=organization, team=t, is_superuser=superuser, persisted=persisted)
|
||||
else:
|
||||
users[p1] = mk_user(p1, organization=organization, team=None, is_superuser=superuser, persisted=persisted)
|
||||
return users
|
||||
|
||||
def generate_teams(organization, persisted, **kwargs):
|
||||
'''generate_teams evalutes a mixed list of Team objects and strings.
|
||||
If a string is encountered a team with that string name is created and added to the lookup dict.
|
||||
If a Team object is encounted the Team.name is used as a key for the lookup dict.
|
||||
'''
|
||||
teams = {}
|
||||
if 'teams' in kwargs and kwargs.get('teams') is not None:
|
||||
for t in kwargs['teams']:
|
||||
if type(t) is Team:
|
||||
teams[t.name] = t
|
||||
else:
|
||||
teams[t] = mk_team(t, organization=organization, persisted=persisted)
|
||||
return teams
|
||||
|
||||
|
||||
class _Mapped(object):
|
||||
'''_Mapped is a helper class that replaces spaces and dashes
|
||||
in the name of an object and assigns the object as an attribute
|
||||
|
||||
input: {'my org': Organization}
|
||||
output: instance.my_org = Organization
|
||||
'''
|
||||
def __init__(self, d):
|
||||
self.d = d
|
||||
for k,v in d.items():
|
||||
k = k.replace(' ', '_')
|
||||
k = k.replace('-', '_')
|
||||
|
||||
setattr(self, k.replace(' ','_'), v)
|
||||
|
||||
def all(self):
|
||||
return self.d.values()
|
||||
|
||||
# create methods are intended to be called directly as needed
|
||||
# or encapsulated by specific factory fixtures in a conftest
|
||||
#
|
||||
|
||||
def create_job_template(name, **kwargs):
|
||||
Objects = namedtuple("Objects", "job_template, inventory, project, credential, job_type")
|
||||
|
||||
org = None
|
||||
proj = None
|
||||
inv = None
|
||||
cred = None
|
||||
job_type = kwargs.get('job_type', 'run')
|
||||
persisted = kwargs.get('persisted', True)
|
||||
|
||||
if 'organization' in kwargs:
|
||||
org = kwargs['organization']
|
||||
if type(org) is not Organization:
|
||||
org = mk_organization(org, '%s-desc'.format(org), persisted=persisted)
|
||||
|
||||
if 'credential' in kwargs:
|
||||
cred = kwargs['credential']
|
||||
if type(cred) is not Credential:
|
||||
cred = mk_credential(cred, persisted=persisted)
|
||||
|
||||
if 'project' in kwargs:
|
||||
proj = kwargs['project']
|
||||
if type(proj) is not Project:
|
||||
proj = mk_project(proj, organization=org, persisted=persisted)
|
||||
|
||||
if 'inventory' in kwargs:
|
||||
inv = kwargs['inventory']
|
||||
if type(inv) is not Inventory:
|
||||
inv = mk_inventory(inv, organization=org, persisted=persisted)
|
||||
|
||||
jt = mk_job_template(name, project=proj,
|
||||
inventory=inv, credential=cred,
|
||||
job_type=job_type, persisted=persisted)
|
||||
|
||||
role_objects = generate_role_objects([org, proj, inv, cred])
|
||||
apply_roles(kwargs.get('roles'), role_objects, persisted)
|
||||
|
||||
return Objects(job_template=jt,
|
||||
project=proj,
|
||||
inventory=inv,
|
||||
credential=cred,
|
||||
job_type=job_type)
|
||||
|
||||
def create_organization(name, **kwargs):
|
||||
Objects = namedtuple("Objects", "organization,teams,users,superusers,projects,labels,notification_templates")
|
||||
|
||||
projects = {}
|
||||
labels = {}
|
||||
notification_templates = {}
|
||||
persisted = kwargs.get('persisted', True)
|
||||
|
||||
org = mk_organization(name, '%s-desc'.format(name), persisted=persisted)
|
||||
|
||||
if 'projects' in kwargs:
|
||||
for p in kwargs['projects']:
|
||||
if type(p) is Project:
|
||||
projects[p.name] = p
|
||||
else:
|
||||
projects[p] = mk_project(p, organization=org, persisted=persisted)
|
||||
|
||||
teams = generate_teams(org, persisted, teams=kwargs.get('teams'))
|
||||
superusers = generate_users(org, teams, True, persisted, superusers=kwargs.get('superusers'))
|
||||
users = generate_users(org, teams, False, persisted, users=kwargs.get('users'))
|
||||
|
||||
if 'labels' in kwargs:
|
||||
for l in kwargs['labels']:
|
||||
if type(l) is Label:
|
||||
labels[l.name] = l
|
||||
else:
|
||||
labels[l] = mk_label(l, organization=org, persisted=persisted)
|
||||
|
||||
if 'notification_templates' in kwargs:
|
||||
for nt in kwargs['notification_templates']:
|
||||
if type(nt) is NotificationTemplate:
|
||||
notification_templates[nt.name] = nt
|
||||
else:
|
||||
notification_templates[nt] = mk_notification_template(nt, organization=org, persisted=persisted)
|
||||
|
||||
role_objects = generate_role_objects([org, superusers, users, teams, projects, labels, notification_templates])
|
||||
apply_roles(kwargs.get('roles'), role_objects, persisted)
|
||||
return Objects(organization=org,
|
||||
superusers=_Mapped(superusers),
|
||||
users=_Mapped(users),
|
||||
teams=_Mapped(teams),
|
||||
projects=_Mapped(projects),
|
||||
labels=_Mapped(labels),
|
||||
notification_templates=_Mapped(notification_templates))
|
||||
|
||||
def create_notification_template(name, **kwargs):
|
||||
Objects = namedtuple("Objects", "notification_template,organization,users,superusers,teams")
|
||||
|
||||
organization = None
|
||||
persisted = kwargs.get('persisted', True)
|
||||
|
||||
if 'organization' in kwargs:
|
||||
org = kwargs['organization']
|
||||
organization = mk_organization(org, '{}-desc'.format(org), persisted=persisted)
|
||||
|
||||
notification_template = mk_notification_template(name, organization=organization, persisted=persisted)
|
||||
|
||||
teams = generate_teams(organization, persisted, teams=kwargs.get('teams'))
|
||||
superusers = generate_users(organization, teams, True, persisted, superusers=kwargs.get('superusers'))
|
||||
users = generate_users(organization, teams, False, persisted, users=kwargs.get('users'))
|
||||
|
||||
role_objects = generate_role_objects([organization, notification_template])
|
||||
apply_roles(kwargs.get('roles'), role_objects, persisted)
|
||||
return Objects(notification_template=notification_template,
|
||||
organization=organization,
|
||||
users=_Mapped(users),
|
||||
superusers=_Mapped(superusers),
|
||||
teams=teams)
|
@ -38,6 +38,12 @@ from awx.main.models.organization import (
|
||||
|
||||
from awx.main.models.notifications import NotificationTemplate
|
||||
|
||||
from awx.main.tests.factories import (
|
||||
create_organization,
|
||||
create_job_template,
|
||||
create_notification_template,
|
||||
)
|
||||
|
||||
'''
|
||||
Disable all django model signals.
|
||||
'''
|
||||
@ -147,18 +153,6 @@ def instance(settings):
|
||||
def organization(instance):
|
||||
return Organization.objects.create(name="test-org", description="test-org-desc")
|
||||
|
||||
@pytest.fixture
|
||||
def organization_factory(instance):
|
||||
def factory(name):
|
||||
try:
|
||||
org = Organization.objects.get(name=name)
|
||||
except Organization.DoesNotExist:
|
||||
org = Organization.objects.create(name=name,
|
||||
description="description for " + name,
|
||||
)
|
||||
return org
|
||||
return factory
|
||||
|
||||
@pytest.fixture
|
||||
def credential():
|
||||
return Credential.objects.create(kind='aws', name='test-cred')
|
||||
@ -282,21 +276,6 @@ def permissions():
|
||||
'update':False, 'delete':False, 'scm_update':False, 'execute':False, 'use':True,},
|
||||
}
|
||||
|
||||
@pytest.fixture
|
||||
def notification_template_factory(organization):
|
||||
def n(name="test-notification_template"):
|
||||
try:
|
||||
notification_template = NotificationTemplate.objects.get(name=name)
|
||||
except NotificationTemplate.DoesNotExist:
|
||||
notification_template = NotificationTemplate(name=name,
|
||||
organization=organization,
|
||||
notification_type="webhook",
|
||||
notification_configuration=dict(url="http://localhost",
|
||||
headers={"Test": "Header"}))
|
||||
notification_template.save()
|
||||
return notification_template
|
||||
return n
|
||||
|
||||
@pytest.fixture
|
||||
def post():
|
||||
def rf(url, data, user=None, middleware=None, **kwargs):
|
||||
@ -474,3 +453,16 @@ def job_template_labels(organization, job_template):
|
||||
job_template.labels.create(name="label-2", organization=organization)
|
||||
|
||||
return job_template
|
||||
|
||||
@pytest.fixture
|
||||
def job_template_factory():
|
||||
return create_job_template
|
||||
|
||||
@pytest.fixture
|
||||
def organization_factory():
|
||||
return create_organization
|
||||
|
||||
@pytest.fixture
|
||||
def notification_template_factory():
|
||||
return create_notification_template
|
||||
|
||||
|
85
awx/main/tests/functional/test_fixture_factories.py
Normal file
85
awx/main/tests/functional/test_fixture_factories.py
Normal file
@ -0,0 +1,85 @@
|
||||
import pytest
|
||||
|
||||
from awx.main.tests.factories import NotUnique
|
||||
|
||||
def test_roles_exc_not_persisted(organization_factory):
|
||||
with pytest.raises(RuntimeError) as exc:
|
||||
organization_factory('test-org', roles=['test-org.admin_role:user1'], persisted=False)
|
||||
assert 'persisted=False' in str(exc.value)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_roles_exc_bad_object(organization_factory):
|
||||
with pytest.raises(KeyError):
|
||||
organization_factory('test-org', roles=['test-project.admin_role:user'])
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_roles_exc_not_unique(organization_factory):
|
||||
with pytest.raises(NotUnique) as exc:
|
||||
organization_factory('test-org', projects=['foo'], teams=['foo'], roles=['foo.admin_role:user'])
|
||||
assert 'not a unique key' in str(exc.value)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_roles_exc_not_assignment(organization_factory):
|
||||
with pytest.raises(RuntimeError) as exc:
|
||||
organization_factory('test-org', projects=['foo'], roles=['foo.admin_role'])
|
||||
assert 'provide an assignment' in str(exc.value)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_roles_exc_not_found(organization_factory):
|
||||
with pytest.raises(RuntimeError) as exc:
|
||||
organization_factory('test-org', users=['user'], projects=['foo'], roles=['foo.admin_role:user.bad_role'])
|
||||
assert 'unable to find' in str(exc.value)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_roles_exc_not_user(organization_factory):
|
||||
with pytest.raises(RuntimeError) as exc:
|
||||
organization_factory('test-org', projects=['foo'], roles=['foo.admin_role:foo'])
|
||||
assert 'unable to add non-user' in str(exc.value)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_org_factory_roles(organization_factory):
|
||||
objects = organization_factory('org_roles_test',
|
||||
teams=['team1', 'team2'],
|
||||
users=['team1:foo', 'bar'],
|
||||
projects=['baz', 'bang'],
|
||||
roles=['team2.member_role:foo',
|
||||
'team1.admin_role:bar',
|
||||
'team1.admin_role:team2.admin_role',
|
||||
'baz.admin_role:foo'])
|
||||
|
||||
assert objects.users.bar in objects.teams.team2.admin_role
|
||||
assert objects.users.foo in objects.projects.baz.admin_role
|
||||
assert objects.users.foo in objects.teams.team1.member_role
|
||||
assert objects.teams.team2.admin_role in objects.teams.team1.admin_role.children.all()
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_org_factory(organization_factory):
|
||||
objects = organization_factory('organization1',
|
||||
teams=['team1'],
|
||||
superusers=['superuser'],
|
||||
users=['admin', 'alice', 'team1:bob'],
|
||||
projects=['proj1'])
|
||||
assert hasattr(objects.users, 'admin')
|
||||
assert hasattr(objects.users, 'alice')
|
||||
assert hasattr(objects.superusers, 'superuser')
|
||||
assert objects.users.bob in objects.teams.team1.member_role.members.all()
|
||||
assert objects.projects.proj1.organization == objects.organization
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_job_template_factory(job_template_factory):
|
||||
jt_objects = job_template_factory('testJT', organization='org1',
|
||||
project='proj1', inventory='inventory1',
|
||||
credential='cred1')
|
||||
assert jt_objects.job_template.name == 'testJT'
|
||||
assert jt_objects.project.name == 'proj1'
|
||||
assert jt_objects.inventory.name == 'inventory1'
|
||||
assert jt_objects.credential.name == 'cred1'
|
||||
assert jt_objects.inventory.organization.name == 'org1'
|
@ -1,7 +1,6 @@
|
||||
import mock # noqa
|
||||
import pytest
|
||||
|
||||
from django.db import transaction
|
||||
from django.core.urlresolvers import reverse
|
||||
from awx.main.models import Project
|
||||
|
||||
@ -9,62 +8,55 @@ from awx.main.models import Project
|
||||
#
|
||||
# Project listing and visibility tests
|
||||
#
|
||||
@pytest.fixture
|
||||
def team_project_list(organization_factory):
|
||||
objects = organization_factory('org-test',
|
||||
superusers=['admin'],
|
||||
users=['team1:alice', 'team2:bob'],
|
||||
teams=['team1', 'team2'],
|
||||
projects=['pteam1', 'pteam2', 'pshared'],
|
||||
roles=['team1.member_role:pteam1.admin_role',
|
||||
'team2.member_role:pteam2.admin_role',
|
||||
'team1.member_role:pshared.admin_role',
|
||||
'team2.member_role:pshared.admin_role'])
|
||||
return objects
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_user_project_list(get, project_factory, organization, admin, alice, bob):
|
||||
def test_user_project_list(get, organization_factory):
|
||||
'List of projects a user has access to, filtered by projects you can also see'
|
||||
|
||||
organization.member_role.members.add(alice, bob)
|
||||
objects = organization_factory('org1',
|
||||
projects=['alice project', 'bob project', 'shared project'],
|
||||
superusers=['admin'],
|
||||
users=['alice', 'bob'],
|
||||
roles=['alice project.admin_role:alice',
|
||||
'bob project.admin_role:bob',
|
||||
'shared project.admin_role:bob',
|
||||
'shared project.admin_role:alice'])
|
||||
|
||||
alice_project = project_factory('alice project')
|
||||
alice_project.admin_role.members.add(alice)
|
||||
|
||||
bob_project = project_factory('bob project')
|
||||
bob_project.admin_role.members.add(bob)
|
||||
|
||||
shared_project = project_factory('shared project')
|
||||
shared_project.admin_role.members.add(alice)
|
||||
shared_project.admin_role.members.add(bob)
|
||||
|
||||
# admins can see all projects
|
||||
assert get(reverse('api:user_projects_list', args=(admin.pk,)), admin).data['count'] == 3
|
||||
assert get(reverse('api:user_projects_list', args=(objects.superusers.admin.pk,)), objects.superusers.admin).data['count'] == 3
|
||||
|
||||
# admins can see everyones projects
|
||||
assert get(reverse('api:user_projects_list', args=(alice.pk,)), admin).data['count'] == 2
|
||||
assert get(reverse('api:user_projects_list', args=(bob.pk,)), admin).data['count'] == 2
|
||||
assert get(reverse('api:user_projects_list', args=(objects.users.alice.pk,)), objects.superusers.admin).data['count'] == 2
|
||||
assert get(reverse('api:user_projects_list', args=(objects.users.bob.pk,)), objects.superusers.admin).data['count'] == 2
|
||||
|
||||
# users can see their own projects
|
||||
assert get(reverse('api:user_projects_list', args=(alice.pk,)), alice).data['count'] == 2
|
||||
assert get(reverse('api:user_projects_list', args=(objects.users.alice.pk,)), objects.users.alice).data['count'] == 2
|
||||
|
||||
# alice should only be able to see the shared project when looking at bobs projects
|
||||
assert get(reverse('api:user_projects_list', args=(bob.pk,)), alice).data['count'] == 1
|
||||
assert get(reverse('api:user_projects_list', args=(objects.users.bob.pk,)), objects.users.alice).data['count'] == 1
|
||||
|
||||
# alice should see all projects they can see when viewing an admin
|
||||
assert get(reverse('api:user_projects_list', args=(admin.pk,)), alice).data['count'] == 2
|
||||
assert get(reverse('api:user_projects_list', args=(objects.superusers.admin.pk,)), objects.users.alice).data['count'] == 2
|
||||
|
||||
|
||||
def setup_test_team_project_list(project_factory, team_factory, admin, alice, bob):
|
||||
team1 = team_factory('team1')
|
||||
team2 = team_factory('team2')
|
||||
|
||||
team1_project = project_factory('team1 project')
|
||||
team1_project.admin_role.parents.add(team1.member_role)
|
||||
|
||||
team2_project = project_factory('team2 project')
|
||||
team2_project.admin_role.parents.add(team2.member_role)
|
||||
|
||||
shared_project = project_factory('shared project')
|
||||
shared_project.admin_role.parents.add(team1.member_role)
|
||||
shared_project.admin_role.parents.add(team2.member_role)
|
||||
|
||||
team1.member_role.members.add(alice)
|
||||
team2.member_role.members.add(bob)
|
||||
return team1, team2
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_team_project_list(get, project_factory, team_factory, admin, alice, bob):
|
||||
'List of projects a team has access to, filtered by projects you can also see'
|
||||
team1, team2 = setup_test_team_project_list(project_factory, team_factory, admin, alice, bob)
|
||||
def test_team_project_list(get, team_project_list):
|
||||
objects = team_project_list
|
||||
|
||||
team1, team2 = objects.teams.team1, objects.teams.team2
|
||||
alice, bob, admin = objects.users.alice, objects.users.bob, objects.superusers.admin
|
||||
|
||||
# admins can see all projects on a team
|
||||
assert get(reverse('api:team_projects_list', args=(team1.pk,)), admin).data['count'] == 2
|
||||
@ -78,12 +70,6 @@ def test_team_project_list(get, project_factory, team_factory, admin, alice, bob
|
||||
assert get(reverse('api:team_projects_list', args=(team2.pk,)), alice).data['count'] == 1
|
||||
team2.read_role.members.remove(alice)
|
||||
|
||||
# Test user endpoints first, very similar tests to test_user_project_list
|
||||
# but permissions are being derived from team membership instead.
|
||||
with transaction.atomic():
|
||||
res = get(reverse('api:user_projects_list', args=(bob.pk,)), alice)
|
||||
assert res.status_code == 403
|
||||
|
||||
# admins can see all projects
|
||||
assert get(reverse('api:user_projects_list', args=(admin.pk,)), admin).data['count'] == 3
|
||||
|
||||
@ -98,17 +84,11 @@ def test_team_project_list(get, project_factory, team_factory, admin, alice, bob
|
||||
assert get(reverse('api:user_projects_list', args=(admin.pk,)), alice).data['count'] == 2
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_team_project_list_fail1(get, project_factory, team_factory, admin, alice, bob):
|
||||
# alice should not be able to see team2 projects because she doesn't have access to team2
|
||||
team1, team2 = setup_test_team_project_list(project_factory, team_factory, admin, alice, bob)
|
||||
res = get(reverse('api:team_projects_list', args=(team2.pk,)), alice)
|
||||
def test_team_project_list_fail1(get, team_project_list):
|
||||
objects = team_project_list
|
||||
res = get(reverse('api:team_projects_list', args=(objects.teams.team2.pk,)), objects.users.alice)
|
||||
assert res.status_code == 403
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_team_project_list_fail2(get, project_factory, team_factory, admin, alice, bob):
|
||||
team1, team2 = setup_test_team_project_list(project_factory, team_factory, admin, alice, bob)
|
||||
# alice should not be able to see bob
|
||||
|
||||
@pytest.mark.parametrize("u,expected_status_code", [
|
||||
('rando', 403),
|
||||
('org_member', 403),
|
||||
|
@ -31,20 +31,22 @@ def test_label_access_superuser(label, user):
|
||||
assert access.can_delete(label)
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_label_access_admin(label, user, organization_factory):
|
||||
def test_label_access_admin(organization_factory):
|
||||
'''can_change because I am an admin of that org'''
|
||||
a = user('admin', False)
|
||||
org_no_members = organization_factory("no_members")
|
||||
org_members = organization_factory("has_members")
|
||||
no_members = organization_factory("no_members")
|
||||
members = organization_factory("has_members",
|
||||
users=['admin'],
|
||||
labels=['test'])
|
||||
|
||||
label.organization.admin_role.members.add(a)
|
||||
org_members.admin_role.members.add(a)
|
||||
label = members.labels.test
|
||||
admin = members.users.admin
|
||||
members.organization.admin_role.members.add(admin)
|
||||
|
||||
access = LabelAccess(user('admin', False))
|
||||
assert not access.can_change(label, {'organization': org_no_members.id})
|
||||
access = LabelAccess(admin)
|
||||
assert not access.can_change(label, {'organization': no_members.organization.id})
|
||||
assert access.can_read(label)
|
||||
assert access.can_change(label, None)
|
||||
assert access.can_change(label, {'organization': org_members.id})
|
||||
assert access.can_change(label, {'organization': members.organization.id})
|
||||
assert access.can_delete(label)
|
||||
|
||||
@pytest.mark.django_db
|
||||
|
@ -25,35 +25,44 @@ def test_notification_template_get_queryset_orgadmin(notification_template, user
|
||||
assert access.get_queryset().count() == 1
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_notification_template_access_superuser(notification_template, user, notification_template_factory):
|
||||
access = NotificationTemplateAccess(user('admin', True))
|
||||
assert access.can_read(notification_template)
|
||||
assert access.can_change(notification_template, None)
|
||||
assert access.can_delete(notification_template)
|
||||
nf = notification_template_factory("test-orphaned")
|
||||
def test_notification_template_access_superuser(notification_template_factory):
|
||||
nf_objects = notification_template_factory('test-orphaned', organization='test', superusers=['admin'])
|
||||
admin = nf_objects.superusers.admin
|
||||
nf = nf_objects.notification_template
|
||||
|
||||
access = NotificationTemplateAccess(admin)
|
||||
assert access.can_read(nf)
|
||||
assert access.can_change(nf, None)
|
||||
assert access.can_delete(nf)
|
||||
|
||||
nf.organization = None
|
||||
nf.save()
|
||||
|
||||
assert access.can_read(nf)
|
||||
assert access.can_change(nf, None)
|
||||
assert access.can_delete(nf)
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_notification_template_access_admin(notification_template, user, organization_factory, notification_template_factory):
|
||||
adm = user('admin', False)
|
||||
other_org = organization_factory('other')
|
||||
present_org = organization_factory('present')
|
||||
notification_template.organization.admin_role.members.add(adm)
|
||||
present_org.admin_role.members.add(adm)
|
||||
def test_notification_template_access_admin(organization_factory, notification_template_factory):
|
||||
other_objects = organization_factory('other')
|
||||
present_objects = organization_factory('present',
|
||||
users=['admin'],
|
||||
notification_templates=['test-notification'],
|
||||
roles=['present.admin_role:admin'])
|
||||
|
||||
access = NotificationTemplateAccess(user('admin', False))
|
||||
notification_template = present_objects.notification_templates.test_notification
|
||||
other_org = other_objects.organization
|
||||
present_org = present_objects.organization
|
||||
admin = present_objects.users.admin
|
||||
|
||||
access = NotificationTemplateAccess(admin)
|
||||
assert not access.can_change(notification_template, {'organization': other_org.id})
|
||||
assert access.can_read(notification_template)
|
||||
assert access.can_change(notification_template, None)
|
||||
assert access.can_change(notification_template, {'organization': present_org.id})
|
||||
assert access.can_delete(notification_template)
|
||||
|
||||
nf = notification_template_factory("test-orphaned")
|
||||
nf.organization = None
|
||||
nf.save()
|
||||
assert not access.can_read(nf)
|
||||
assert not access.can_change(nf, None)
|
||||
assert not access.can_delete(nf)
|
||||
|
Loading…
Reference in New Issue
Block a user