mirror of
https://github.com/ansible/awx.git
synced 2026-05-07 09:27:36 -02:30
Further module conversion changes, unit test changes
Multiple module changes Added on_change callback Added head_endpoint Added additional error returns Respond with a try an ID message if multiple assets found by name via return_none_on_404 kwarg Diferentiated between login and logout token errors Added is_job_done method
This commit is contained in:
@@ -111,6 +111,86 @@ def run_module(request):
|
||||
return rf
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def run_converted_module(request):
|
||||
# A placeholder to use while modules get converted
|
||||
def rf(module_name, module_params, request_user):
|
||||
|
||||
def new_request(self, method, url, **kwargs):
|
||||
kwargs_copy = kwargs.copy()
|
||||
if 'data' in kwargs:
|
||||
if not isinstance(kwargs['data'], dict):
|
||||
kwargs_copy['data'] = json.loads(kwargs['data'])
|
||||
else:
|
||||
kwargs_copy['data'] = kwargs['data']
|
||||
if 'params' in kwargs and method == 'GET':
|
||||
# query params for GET are handled a bit differently by
|
||||
# tower-cli and python requests as opposed to REST framework APIRequestFactory
|
||||
kwargs_copy.setdefault('data', {})
|
||||
if isinstance(kwargs['params'], dict):
|
||||
kwargs_copy['data'].update(kwargs['params'])
|
||||
elif isinstance(kwargs['params'], list):
|
||||
for k, v in kwargs['params']:
|
||||
kwargs_copy['data'][k] = v
|
||||
|
||||
# make request
|
||||
rf = _request(method.lower())
|
||||
django_response = rf(url, user=request_user, expect=None, **kwargs_copy)
|
||||
|
||||
# requests library response object is different from the Django response, but they are the same concept
|
||||
# this converts the Django response object into a requests response object for consumption
|
||||
resp = Response()
|
||||
py_data = django_response.data
|
||||
sanitize_dict(py_data)
|
||||
resp._content = bytes(json.dumps(django_response.data), encoding='utf8')
|
||||
resp.status_code = django_response.status_code
|
||||
|
||||
if request.config.getoption('verbose') > 0:
|
||||
logger.info(
|
||||
'%s %s by %s, code:%s',
|
||||
method, '/api/' + url.split('/api/')[1],
|
||||
request_user.username, resp.status_code
|
||||
)
|
||||
|
||||
return resp
|
||||
|
||||
def new_open(self, method, url, **kwargs):
|
||||
r = new_request(self, method, url, **kwargs)
|
||||
return mock.MagicMock(read=mock.MagicMock(return_value=r._content), status=r.status_code)
|
||||
|
||||
stdout_buffer = io.StringIO()
|
||||
# Requies specific PYTHONPATH, see docs
|
||||
# Note that a proper Ansiballz explosion of the modules will have an import path like:
|
||||
# ansible_collections.awx.awx.plugins.modules.{}
|
||||
# We should consider supporting that in the future
|
||||
resource_module = importlib.import_module('plugins.modules.{0}'.format(module_name))
|
||||
|
||||
if not isinstance(module_params, dict):
|
||||
raise RuntimeError('Module params must be dict, got {0}'.format(type(module_params)))
|
||||
|
||||
# Ansible params can be passed as an invocation argument or over stdin
|
||||
# this short circuits within the AnsibleModule interface
|
||||
def mock_load_params(self):
|
||||
self.params = module_params
|
||||
|
||||
with mock.patch.object(resource_module.TowerModule, '_load_params', new=mock_load_params):
|
||||
# Call the test utility (like a mock server) instead of issuing HTTP requests
|
||||
with mock.patch('ansible.module_utils.urls.Request.open', new=new_open):
|
||||
with mock.patch('tower_cli.api.Session.request', new=new_request):
|
||||
# Ansible modules return data to the mothership over stdout
|
||||
with redirect_stdout(stdout_buffer):
|
||||
try:
|
||||
resource_module.main()
|
||||
except SystemExit:
|
||||
pass # A system exit indicates successful execution
|
||||
|
||||
module_stdout = stdout_buffer.getvalue().strip()
|
||||
result = json.loads(module_stdout)
|
||||
return result
|
||||
|
||||
return rf
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def organization():
|
||||
return Organization.objects.create(name='Default')
|
||||
|
||||
@@ -63,9 +63,9 @@ def test_create_vault_credential(run_module, admin_user):
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_create_custom_credential_type(run_module, admin_user):
|
||||
def test_create_custom_credential_type(run_converted_module, admin_user):
|
||||
# Example from docs
|
||||
result = run_module('tower_credential_type', dict(
|
||||
result = run_converted_module('tower_credential_type', dict(
|
||||
name='Nexus',
|
||||
description='Credentials type for Nexus',
|
||||
kind='cloud',
|
||||
@@ -79,8 +79,7 @@ def test_create_custom_credential_type(run_module, admin_user):
|
||||
ct = CredentialType.objects.get(name='Nexus')
|
||||
result.pop('invocation')
|
||||
assert result == {
|
||||
"credential_type": "Nexus",
|
||||
"state": "present",
|
||||
"name": "Nexus",
|
||||
"id": ct.pk,
|
||||
"changed": True
|
||||
}
|
||||
|
||||
@@ -7,20 +7,31 @@ from awx.main.models import Organization
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_create_organization(run_module, admin_user):
|
||||
def test_create_organization(run_converted_module, admin_user):
|
||||
|
||||
module_args = {'name': 'foo', 'description': 'barfoo', 'state': 'present'}
|
||||
module_args = {
|
||||
'name': 'foo',
|
||||
'description': 'barfoo',
|
||||
'state': 'present',
|
||||
'max_hosts': '0',
|
||||
'tower_host': None,
|
||||
'tower_username': None,
|
||||
'tower_password': None,
|
||||
'validate_certs': None,
|
||||
'tower_oauthtoken': None,
|
||||
'tower_config_file': None,
|
||||
'custom_virtualenv': None
|
||||
}
|
||||
|
||||
result = run_module('tower_organization', module_args, admin_user)
|
||||
result = run_converted_module('tower_organization', module_args, admin_user)
|
||||
assert result.get('changed'), result
|
||||
|
||||
org = Organization.objects.get(name='foo')
|
||||
|
||||
assert result == {
|
||||
"organization": "foo",
|
||||
"state": "present",
|
||||
"id": org.id,
|
||||
"changed": True,
|
||||
"name": "foo",
|
||||
"id": org.id,
|
||||
"invocation": {
|
||||
"module_args": module_args
|
||||
}
|
||||
@@ -30,10 +41,10 @@ def test_create_organization(run_module, admin_user):
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_create_organization_with_venv(run_module, admin_user, mocker):
|
||||
def test_create_organization_with_venv(run_converted_module, admin_user, mocker):
|
||||
path = '/var/lib/awx/venv/custom-venv/foobar13489435/'
|
||||
with mocker.patch('awx.main.models.mixins.get_custom_venv_choices', return_value=[path]):
|
||||
result = run_module('tower_organization', {
|
||||
result = run_converted_module('tower_organization', {
|
||||
'name': 'foo',
|
||||
'custom_virtualenv': path,
|
||||
'state': 'present'
|
||||
@@ -44,8 +55,7 @@ def test_create_organization_with_venv(run_module, admin_user, mocker):
|
||||
result.pop('invocation')
|
||||
|
||||
assert result == {
|
||||
"organization": "foo",
|
||||
"state": "present",
|
||||
"name": "foo",
|
||||
"id": org.id
|
||||
}
|
||||
|
||||
|
||||
66
awx_collection/test/awx/test_team.py
Normal file
66
awx_collection/test/awx/test_team.py
Normal file
@@ -0,0 +1,66 @@
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
import pytest
|
||||
|
||||
from awx.main.models import Organization, Team
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_create_team(run_converted_module, admin_user):
|
||||
org = Organization.objects.create(name='foo')
|
||||
|
||||
result = run_converted_module('tower_team', {
|
||||
'name': 'foo_team',
|
||||
'description': 'fooin around',
|
||||
'state': 'present',
|
||||
'organization': 'foo'
|
||||
}, admin_user)
|
||||
|
||||
team = Team.objects.filter(name='foo_team').first()
|
||||
|
||||
result.pop('invocation')
|
||||
assert result == {
|
||||
"changed": True,
|
||||
"name": "foo_team",
|
||||
"id": team.id if team else None,
|
||||
}
|
||||
team = Team.objects.get(name='foo_team')
|
||||
assert team.description == 'fooin around'
|
||||
assert team.organization_id == org.id
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_modify_team(run_converted_module, admin_user):
|
||||
org = Organization.objects.create(name='foo')
|
||||
team = Team.objects.create(
|
||||
name='foo_team',
|
||||
organization=org,
|
||||
description='flat foo'
|
||||
)
|
||||
assert team.description == 'flat foo'
|
||||
|
||||
result = run_converted_module('tower_team', {
|
||||
'name': 'foo_team',
|
||||
'description': 'fooin around',
|
||||
'organization': 'foo'
|
||||
}, admin_user)
|
||||
team.refresh_from_db()
|
||||
result.pop('invocation')
|
||||
assert result == {
|
||||
"id": team.id,
|
||||
"changed": True
|
||||
}
|
||||
assert team.description == 'fooin around'
|
||||
|
||||
# 2nd modification, should cause no change
|
||||
result = run_converted_module('tower_team', {
|
||||
'name': 'foo_team',
|
||||
'description': 'fooin around',
|
||||
'organization': 'foo'
|
||||
}, admin_user)
|
||||
result.pop('invocation')
|
||||
assert result == {
|
||||
"id": team.id,
|
||||
"changed": False
|
||||
}
|
||||
Reference in New Issue
Block a user