add assertion to test on number of queries made (#9)

* add assertions around access to resulting job

there is a problem getting the job w/ the user that launched it

add more assertions to bulk tests (#11)

dig more into the results and assert on results
also, use a fixture that already implemented the "max queries" thing

fix ansible collection sanity tests (#12)
This commit is contained in:
Elijah DeLee
2023-02-23 09:00:54 -05:00
parent 7aad16964c
commit 2becc5dda9
5 changed files with 38 additions and 27 deletions

View File

@@ -6,23 +6,15 @@ from awx.api.versioning import reverse
import json import json
from contextlib import contextmanager from contextlib import contextmanager
from django.test.utils import CaptureQueriesContext
from django.db import connections from django.db import connections
from awx.main.models.jobs import JobTemplate from awx.main.models.jobs import JobTemplate
from awx.main.models import Organization, Inventory from awx.main.models import Organization, Inventory, WorkflowJob
@contextmanager
def withAssertNumQueriesLessThan(num_queries):
with CaptureQueriesContext(connections['default']) as context:
yield
fail_msg = f"\r\n{json.dumps(context.captured_queries, indent=4)}"
assert len(context.captured_queries) < num_queries, fail_msg
@pytest.mark.django_db @pytest.mark.django_db
@pytest.mark.parametrize('num_hosts, num_queries', [(9, 15), (99, 20), (999, 30)]) @pytest.mark.parametrize('num_hosts, num_queries', [(9, 15), (99, 20), (999, 30)])
def test_bulk_host_create_num_queries(organization, inventory, post, get, user, num_hosts, num_queries): def test_bulk_host_create_num_queries(organization, inventory, post, get, user, num_hosts, num_queries, django_assert_max_num_queries):
''' '''
If I am a... If I am a...
org admin org admin
@@ -45,7 +37,7 @@ def test_bulk_host_create_num_queries(organization, inventory, post, get, user,
for u in [org_admin, inventory_admin, org_inv_admin, superuser]: for u in [org_admin, inventory_admin, org_inv_admin, superuser]:
hosts = [{'name': uuid4()} for i in range(num_hosts)] hosts = [{'name': uuid4()} for i in range(num_hosts)]
with withAssertNumQueriesLessThan(num_queries): with django_assert_max_num_queries(num_queries):
bulk_host_create_response = post(reverse('api:bulk_host_create'), {'inventory': inventory.id, 'hosts': hosts}, u, expect=201).data bulk_host_create_response = post(reverse('api:bulk_host_create'), {'inventory': inventory.id, 'hosts': hosts}, u, expect=201).data
assert len(bulk_host_create_response['hosts']) == len(hosts), f"unexpected number of hosts created for user {u}" assert len(bulk_host_create_response['hosts']) == len(hosts), f"unexpected number of hosts created for user {u}"
@@ -89,20 +81,36 @@ def test_bulk_host_create_rbac(organization, inventory, post, get, user):
@pytest.mark.django_db @pytest.mark.django_db
def test_bulk_job_launch(job_template, organization, inventory, project, credential, post, get, user): @pytest.mark.parametrize('num_jobs, num_queries', [(9, 30), (99, 35)])
def test_bulk_job_launch_queries(job_template, organization, inventory, project, post, get, user, num_jobs, num_queries, django_assert_max_num_queries):
''' '''
if I have access to the unified job template if I have access to the unified job template
... I can launch the bulk job ... I can launch the bulk job
... and the number of queries should NOT scale with the number of jobs
''' '''
normal_user = user('normal_user', False) normal_user = user('normal_user', False)
jt = JobTemplate.objects.create(name='my-jt', inventory=inventory, project=project, playbook='helloworld.yml') org_admin = user('org_admin', False)
jt.save() jt = JobTemplate.objects.create(name='my-jt', ask_inventory_on_launch=True, project=project, playbook='helloworld.yml')
organization.member_role.members.add(normal_user) organization.member_role.members.add(normal_user)
organization.admin_role.members.add(org_admin)
jt.execute_role.members.add(normal_user) jt.execute_role.members.add(normal_user)
bulk_job_launch_response = post( inventory.use_role.members.add(normal_user)
reverse('api:bulk_job_launch'), {'name': 'Bulk Job Launch', 'jobs': [{'unified_job_template': jt.id}]}, normal_user, expect=201 jt.save()
).data inventory.save()
assert bulk_job_launch_response['id'] == 1 jobs = [{'unified_job_template': jt.id, 'inventory': inventory.id} for _ in range(num_jobs)]
# This is not working, we need to figure that out if we want to include tests for more jobs
# with mock.patch('awx.api.serializers.settings.BULK_JOB_MAX_LAUNCH', num_jobs + 1):
with django_assert_max_num_queries(num_queries):
bulk_job_launch_response = post(reverse('api:bulk_job_launch'), {'name': 'Bulk Job Launch', 'jobs': jobs}, normal_user, expect=201).data
for u in (org_admin,): # TODO normal_user not working because launched_by not getting set in the tests...does happen in real request
bulk_job = get(bulk_job_launch_response['url'], u, expect=200).data
assert organization.id == bulk_job['summary_fields']['organization']['id']
resp = get(bulk_job_launch_response['related']['workflow_nodes'], u)
assert resp.data['count'] == num_jobs
for item in resp.data['results']:
assert item["unified_job_template"] == jt.id
@pytest.mark.django_db @pytest.mark.django_db
@@ -167,7 +175,9 @@ def test_bulk_job_launch_specific_org(job_template, organization, inventory, pro
bulk_job_launch_response = post( bulk_job_launch_response = post(
reverse('api:bulk_job_launch'), {'name': 'Bulk Job Launch', 'jobs': [{'unified_job_template': jt.id}], 'organization': org1.id}, normal_user, expect=201 reverse('api:bulk_job_launch'), {'name': 'Bulk Job Launch', 'jobs': [{'unified_job_template': jt.id}], 'organization': org1.id}, normal_user, expect=201
).data ).data
assert bulk_job_launch_response['id'] == 1 bulk_job_id = bulk_job_launch_response['id']
bulk_job_obj = WorkflowJob.objects.filter(id=bulk_job_id, is_bulk_job=True).first()
assert org1.id == bulk_job_obj.organization.id
@pytest.mark.django_db @pytest.mark.django_db

View File

@@ -68,6 +68,7 @@ Notable releases of the `awx.awx` collection:
- 7.0.0 is intended to be identical to the content prior to the migration, aside from changes necessary to function as a collection. - 7.0.0 is intended to be identical to the content prior to the migration, aside from changes necessary to function as a collection.
- 11.0.0 has no non-deprecated modules that depend on the deprecated `tower-cli` [PyPI](https://pypi.org/project/ansible-tower-cli/). - 11.0.0 has no non-deprecated modules that depend on the deprecated `tower-cli` [PyPI](https://pypi.org/project/ansible-tower-cli/).
- 19.2.1 large renaming purged "tower" names (like options and module names), adding redirects for old names - 19.2.1 large renaming purged "tower" names (like options and module names), adding redirects for old names
- 21.11.0 "tower" modules deprecated and symlinks removed.
- 0.0.1-devel is the version you should see if installing from source, which is intended for development and expected to be unstable. - 0.0.1-devel is the version you should see if installing from source, which is intended for development and expected to be unstable.
The following notes are changes that may require changes to playbooks: The following notes are changes that may require changes to playbooks:

View File

@@ -29,7 +29,7 @@ options:
description: description:
- The name to use for the host. - The name to use for the host.
type: str type: str
require: True required: True
description: description:
description: description:
- The description to use for the host. - The description to use for the host.
@@ -70,7 +70,7 @@ import json
def main(): def main():
# Any additional arguments that are not fields of the item can be added here # Any additional arguments that are not fields of the item can be added here
argument_spec = dict( argument_spec = dict(
hosts=dict(required=True, type='list'), hosts=dict(required=True, type='list', elements='dict'),
inventory=dict(required=True, type='int'), inventory=dict(required=True, type='int'),
) )
@@ -82,8 +82,8 @@ def main():
hosts = module.params.get('hosts') hosts = module.params.get('hosts')
for h in hosts: for h in hosts:
if 'variables' in h: if 'variables' in h:
h['variables'] = json.dumps(h['variables']) h['variables'] = json.dumps(h['variables'])
# Launch the jobs # Launch the jobs
result = module.post_endpoint("bulk/host_create", data={"inventory": inventory, "hosts": hosts}) result = module.post_endpoint("bulk/host_create", data={"inventory": inventory, "hosts": hosts})

View File

@@ -205,7 +205,7 @@ from ..module_utils.controller_api import ControllerAPIModule
def main(): def main():
# Any additional arguments that are not fields of the item can be added here # Any additional arguments that are not fields of the item can be added here
argument_spec = dict( argument_spec = dict(
jobs=dict(required=True, type='list'), jobs=dict(required=True, type='list', elements='dict'),
name=dict(), name=dict(),
description=dict(), description=dict(),
organization=dict(type='int'), organization=dict(type='int'),
@@ -217,7 +217,6 @@ def main():
skip_tags=dict(), skip_tags=dict(),
wait=dict(required=False, default=True, type='bool'), wait=dict(required=False, default=True, type='bool'),
interval=dict(required=False, default=2.0, type='float'), interval=dict(required=False, default=2.0, type='float'),
timeout=dict(required=False, default=None, type='int'),
) )
# Create a module for ourselves # Create a module for ourselves

View File

@@ -6,6 +6,7 @@ import pytest
from awx.main.models import WorkflowJob from awx.main.models import WorkflowJob
@pytest.mark.django_db @pytest.mark.django_db
def test_bulk_job_launch(run_module, admin_user, job_template): def test_bulk_job_launch(run_module, admin_user, job_template):
jobs = [dict(unified_job_template=job_template.id)] jobs = [dict(unified_job_template=job_template.id)]