summaryrefslogtreecommitdiffstats
path: root/awx_collection
diff options
context:
space:
mode:
authorChris Meyers <chris.meyers.fsu@gmail.com>2023-12-15 21:55:19 +0100
committerChris Meyers <chrismeyersfsu@users.noreply.github.com>2023-12-18 18:00:50 +0100
commit6119b33a50e1ee7dd79ded9d8dbd4139408551ca (patch)
treecfebf1a61e680b0522943f53799a400dbc0e57ae /awx_collection
parentUse filtering/sorting from django-ansible-base (#14726) (diff)
downloadawx-6119b33a50e1ee7dd79ded9d8dbd4139408551ca.tar.xz
awx-6119b33a50e1ee7dd79ded9d8dbd4139408551ca.zip
add awx collection export tests
* Basic export tests * Added test that highlights a problem with running Schedule exports as non-root user. We rely on the POST key in the OPTIONS response to determine the fields to export for a resource. The POST key is not present if a user does NOT have create privileges. * Fixed up forwarding all headers from the API server back to the test code. This was causing a problem in awxkit code that checks for allowed HTTP Verbs in the headers.
Diffstat (limited to 'awx_collection')
-rw-r--r--awx_collection/test/awx/conftest.py66
-rw-r--r--awx_collection/test/awx/test_export.py154
2 files changed, 211 insertions, 9 deletions
diff --git a/awx_collection/test/awx/conftest.py b/awx_collection/test/awx/conftest.py
index 626f859363..9d1116644e 100644
--- a/awx_collection/test/awx/conftest.py
+++ b/awx_collection/test/awx/conftest.py
@@ -18,7 +18,20 @@ import pytest
from ansible.module_utils.six import raise_from
from awx.main.tests.functional.conftest import _request
-from awx.main.models import Organization, Project, Inventory, JobTemplate, Credential, CredentialType, ExecutionEnvironment, UnifiedJob
+from awx.main.tests.functional.conftest import credentialtype_scm, credentialtype_ssh # noqa: F401; pylint: disable=unused-variable
+from awx.main.models import (
+ Organization,
+ Project,
+ Inventory,
+ JobTemplate,
+ Credential,
+ CredentialType,
+ ExecutionEnvironment,
+ UnifiedJob,
+ WorkflowJobTemplate,
+ NotificationTemplate,
+ Schedule,
+)
from django.db import transaction
@@ -123,7 +136,7 @@ def run_module(request, collection_import):
sanitize_dict(py_data)
resp._content = bytes(json.dumps(django_response.data), encoding='utf8')
resp.status_code = django_response.status_code
- resp.headers = {'X-API-Product-Name': 'AWX', 'X-API-Product-Version': '0.0.1-devel'}
+ resp.headers = dict(django_response.headers)
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)
@@ -236,10 +249,8 @@ def job_template(project, inventory):
@pytest.fixture
-def machine_credential(organization):
- ssh_type = CredentialType.defaults['ssh']()
- ssh_type.save()
- return Credential.objects.create(credential_type=ssh_type, name='machine-cred', inputs={'username': 'test_user', 'password': 'pas4word'})
+def machine_credential(credentialtype_ssh, organization): # noqa: F811
+ return Credential.objects.create(credential_type=credentialtype_ssh, name='machine-cred', inputs={'username': 'test_user', 'password': 'pas4word'})
@pytest.fixture
@@ -253,9 +264,7 @@ def vault_credential(organization):
def kube_credential():
ct = CredentialType.defaults['kubernetes_bearer_token']()
ct.save()
- return Credential.objects.create(
- credential_type=ct, name='kube-cred', inputs={'host': 'my.cluster', 'bearer_token': 'my-token', 'verify_ssl': False}
- )
+ return Credential.objects.create(credential_type=ct, name='kube-cred', inputs={'host': 'my.cluster', 'bearer_token': 'my-token', 'verify_ssl': False})
@pytest.fixture
@@ -288,3 +297,42 @@ def mock_has_unpartitioned_events():
# We mock this out to circumvent the migration query.
with mock.patch.object(UnifiedJob, 'has_unpartitioned_events', new=False) as _fixture:
yield _fixture
+
+
+@pytest.fixture
+def workflow_job_template(organization, inventory):
+ return WorkflowJobTemplate.objects.create(name='test-workflow_job_template', organization=organization, inventory=inventory)
+
+
+@pytest.fixture
+def notification_template(organization):
+ return NotificationTemplate.objects.create(
+ name='test-notification_template',
+ organization=organization,
+ notification_type="webhook",
+ notification_configuration=dict(
+ url="http://localhost",
+ username="",
+ password="",
+ headers={
+ "Test": "Header",
+ },
+ ),
+ )
+
+
+@pytest.fixture
+def scm_credential(credentialtype_scm, organization): # noqa: F811
+ return Credential.objects.create(
+ credential_type=credentialtype_scm, name='scm-cred', inputs={'username': 'optimus', 'password': 'prime'}, organization=organization
+ )
+
+
+@pytest.fixture
+def rrule():
+ return 'DTSTART:20151117T050000Z RRULE:FREQ=DAILY;INTERVAL=1;COUNT=1'
+
+
+@pytest.fixture
+def schedule(job_template, rrule):
+ return Schedule.objects.create(unified_job_template=job_template, name='test-sched', rrule=rrule)
diff --git a/awx_collection/test/awx/test_export.py b/awx_collection/test/awx/test_export.py
new file mode 100644
index 0000000000..a70b451df4
--- /dev/null
+++ b/awx_collection/test/awx/test_export.py
@@ -0,0 +1,154 @@
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import pytest
+
+from awx.main.models.execution_environments import ExecutionEnvironment
+from awx.main.models.jobs import JobTemplate
+
+from awx.main.tests.functional.conftest import user, system_auditor # noqa: F401; pylint: disable=unused-import
+
+
+ASSETS = set([
+ "users",
+ "organizations",
+ "teams",
+ "credential_types",
+ "credentials",
+ "notification_templates",
+ "projects",
+ "inventory",
+ "inventory_sources",
+ "job_templates",
+ "workflow_job_templates",
+ "execution_environments",
+ "applications",
+ "schedules",
+])
+
+
+@pytest.fixture
+def job_template(project, inventory, organization, machine_credential):
+ jt = JobTemplate.objects.create(name='test-jt', project=project, inventory=inventory, organization=organization, playbook='helloworld.yml')
+ jt.credentials.add(machine_credential)
+ jt.save()
+ return jt
+
+
+@pytest.fixture
+def execution_environment(organization):
+ return ExecutionEnvironment.objects.create(name="test-ee", description="test-ee", managed=False, organization=organization)
+
+
+def find_by(result, name, key, value):
+ for c in result[name]:
+ if c[key] == value:
+ return c
+ values = [c.get(key, None) for c in result[name]]
+ raise ValueError(f"Failed to find assets['{name}'][{key}] = '{value}' valid values are {values}")
+
+
+@pytest.mark.django_db
+def test_export(run_module, admin_user):
+ """
+ There should be nothing to export EXCEPT the admin user.
+ """
+ result = run_module('export', dict(all=True), admin_user)
+ assert not result.get('failed', False), result.get('msg', result)
+ assets = result['assets']
+
+ assert set(result['assets'].keys()) == ASSETS
+
+ u = find_by(assets, 'users', 'username', 'admin')
+ assert u['is_superuser'] is True
+
+ all_assets_except_users = {k: v for k, v in assets.items() if k != 'users'}
+
+ for k, v in all_assets_except_users.items():
+ assert v == [], f"Expected resource {k} to be empty. Instead it is {v}"
+
+
+@pytest.mark.django_db
+def test_export_simple(
+ run_module,
+ organization,
+ project,
+ inventory,
+ job_template,
+ scm_credential,
+ machine_credential,
+ workflow_job_template,
+ execution_environment,
+ notification_template,
+ rrule,
+ schedule,
+ admin_user,
+):
+ """
+ TODO: Ensure there aren't _more_ results in each resource than we expect
+ """
+ result = run_module('export', dict(all=True), admin_user)
+ assert not result.get('failed', False), result.get('msg', result)
+ assets = result['assets']
+
+ u = find_by(assets, 'users', 'username', 'admin')
+ assert u['is_superuser'] is True
+
+ find_by(assets, 'organizations', 'name', 'Default')
+
+ r = find_by(assets, 'credentials', 'name', 'scm-cred')
+ assert r['credential_type']['kind'] == 'scm'
+ assert r['credential_type']['name'] == 'Source Control'
+
+ r = find_by(assets, 'credentials', 'name', 'machine-cred')
+ assert r['credential_type']['kind'] == 'ssh'
+ assert r['credential_type']['name'] == 'Machine'
+
+ r = find_by(assets, 'job_templates', 'name', 'test-jt')
+ assert r['natural_key']['organization']['name'] == 'Default'
+ assert r['inventory']['name'] == 'test-inv'
+ assert r['project']['name'] == 'test-proj'
+
+ find_by(r['related'], 'credentials', 'name', 'machine-cred')
+
+ r = find_by(assets, 'inventory', 'name', 'test-inv')
+ assert r['organization']['name'] == 'Default'
+
+ r = find_by(assets, 'projects', 'name', 'test-proj')
+ assert r['organization']['name'] == 'Default'
+
+ r = find_by(assets, 'workflow_job_templates', 'name', 'test-workflow_job_template')
+ assert r['natural_key']['organization']['name'] == 'Default'
+ assert r['inventory']['name'] == 'test-inv'
+
+ r = find_by(assets, 'execution_environments', 'name', 'test-ee')
+ assert r['organization']['name'] == 'Default'
+
+ r = find_by(assets, 'schedules', 'name', 'test-sched')
+ assert r['rrule'] == rrule
+
+ r = find_by(assets, 'notification_templates', 'name', 'test-notification_template')
+ assert r['organization']['name'] == 'Default'
+ assert r['notification_configuration']['url'] == 'http://localhost'
+
+
+@pytest.mark.django_db
+def test_export_system_auditor(run_module, schedule, system_auditor): # noqa: F811
+ """
+ This test illustrates that deficiency of export when ran as non-root user (i.e. system auditor).
+ The OPTIONS endpoint does NOT return POST for a system auditor. This is bad for the export code
+ because it relies on crawling the OPTIONS POST response to determine the fields to export.
+ """
+ result = run_module('export', dict(all=True), system_auditor)
+ assert result.get('failed', False), result.get('msg', result)
+
+ assert 'Failed to export assets substring not found' in result['msg'], (
+ 'If you found this error then you have probably fixed a feature! The export code attempts to assertain the POST fields from the `description` field,'
+ ' but both the API side and the client inference code are lacking.'
+ )
+
+ # r = result['assets']['schedules'][0]
+ # assert r['natural_key']['name'] == 'test-sched'
+
+ # assert 'rrule' not in r, 'If you found this error then you have probably fixed a feature! We WANT rrule to be found in the export schedule payload.'