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:
beeankha
2020-01-24 11:30:16 -05:00
parent 68926dad27
commit 7c0ad461a5
10 changed files with 224 additions and 47 deletions

View File

@@ -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')

View File

@@ -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
}

View File

@@ -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
}

View 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
}