Пример #1
0
    def test_optional_object_var(self):
        """
        Test that an optional Object variable field behaves as expected.
        """
        with self.settings(JOBS_ROOT=os.path.join(settings.BASE_DIR, "extras/tests/dummy_jobs")):
            module = "test_object_var_optional"
            name = "TestOptionalObjectVar"
            job_class = get_job(f"local/{module}/{name}")

            # Prepare the job data
            job_result = JobResult.objects.create(
                name=job_class.class_path,
                obj_type=self.job_content_type,
                user=None,
                job_id=uuid.uuid4(),
            )

            data = {"region": None}

            # Run the job without the optional var provided
            run_job(data=data, request=self.request, commit=True, job_result_pk=job_result.pk)
            job_result.refresh_from_db()

            info_log = JobLogEntry.objects.filter(
                job_result=job_result, log_level=LogLevelChoices.LOG_INFO, grouping="run"
            ).first()

            # Assert stuff
            self.assertEqual(job_result.status, JobResultStatusChoices.STATUS_COMPLETED)
            self.assertEqual(info_log.log_object, None)
            self.assertEqual(info_log.message, "The Region if any that the user provided.")
            self.assertEqual(job_result.data["output"], "\nNice Region (or not)!")
Пример #2
0
    def test_job_pass_with_run_job_directly(self):
        """
        Job test with pass result calling run_job directly in order to test for backwards stability of its API.

        Because calling run_job directly used to be the best practice for testing jobs, we want to ensure that calling
        it still works even if we ever change the run_job call in the run_job_for_testing wrapper.
        """
        module = "test_pass"
        name = "TestPass"
        job_class, job_model = get_job_class_and_model(module, name)
        job_model.enabled = True
        job_model.validated_save()
        job_content_type = ContentType.objects.get(app_label="extras",
                                                   model="job")
        job_result = JobResult.objects.create(
            name=job_model.class_path,
            obj_type=job_content_type,
            job_model=job_model,
            user=None,
            job_id=uuid.uuid4(),
        )
        run_job(data={},
                request=None,
                commit=False,
                job_result_pk=job_result.pk)
        job_result = create_job_result_and_run_job(module, name, commit=False)
        self.assertEqual(job_result.status,
                         JobResultStatusChoices.STATUS_COMPLETED)
Пример #3
0
    def test_job_fail(self):
        """
        Job test with fail result.
        """
        with self.settings(JOBS_ROOT=os.path.join(settings.BASE_DIR,
                                                  "extras/tests/dummy_jobs")):

            module = "test_fail"
            name = "TestFail"
            job_class = get_job(f"local/{module}/{name}")
            job_content_type = ContentType.objects.get(app_label="extras",
                                                       model="job")
            job_result = JobResult.objects.create(
                name=job_class.class_path,
                obj_type=job_content_type,
                user=None,
                job_id=uuid.uuid4(),
            )
            run_job(data={},
                    request=None,
                    commit=False,
                    job_result_pk=job_result.pk)
            job_result.refresh_from_db()
            self.assertEqual(job_result.status,
                             JobResultStatusChoices.STATUS_ERRORED)
Пример #4
0
    def test_ready_only_job_pass(self):
        """
        Job read only test with pass result.
        """
        with self.settings(JOBS_ROOT=os.path.join(settings.BASE_DIR,
                                                  "extras/tests/dummy_jobs")):

            module = "test_read_only_pass"
            name = "TestReadOnlyPass"
            job_class = get_job(f"local/{module}/{name}")
            job_content_type = ContentType.objects.get(app_label="extras",
                                                       model="job")

            job_result = JobResult.objects.create(
                name=job_class.class_path,
                obj_type=job_content_type,
                user=None,
                job_id=uuid.uuid4(),
            )

            run_job(data={},
                    request=None,
                    commit=False,
                    job_result_pk=job_result.pk)
            job_result.refresh_from_db()
            self.assertEqual(job_result.status,
                             JobResultStatusChoices.STATUS_COMPLETED)
            self.assertEqual(Site.objects.count(),
                             0)  # Ensure DB transaction was aborted
Пример #5
0
    def test_read_only_job_fail(self):
        """
        Job read only test with fail result.
        """
        with self.settings(JOBS_ROOT=os.path.join(settings.BASE_DIR,
                                                  "extras/tests/dummy_jobs")):

            module = "test_read_only_fail"
            name = "TestReadOnlyFail"
            job_class = get_job(f"local/{module}/{name}")
            job_content_type = ContentType.objects.get(app_label="extras",
                                                       model="job")
            job_result = JobResult.objects.create(
                name=job_class.class_path,
                obj_type=job_content_type,
                user=None,
                job_id=uuid.uuid4(),
            )
            run_job(data={},
                    request=None,
                    commit=False,
                    job_result_pk=job_result.pk)
            job_result.refresh_from_db()
            self.assertEqual(job_result.status,
                             JobResultStatusChoices.STATUS_ERRORED)
            self.assertEqual(Site.objects.count(),
                             0)  # Ensure DB transaction was aborted
            # Also ensure the standard log message about aborting the transaction is *not* present
            self.assertNotEqual(
                job_result.data["run"]["log"][-1][-1],
                "Database changes have been reverted due to error.")
Пример #6
0
    def test_run_job_fail(self):
        """Test that file upload succeeds; job FAILS; files deleted."""
        with self.settings(JOBS_ROOT=os.path.join(settings.BASE_DIR, "extras/tests/dummy_jobs")):
            job_name = "local/test_file_upload_fail/TestFileUploadFail"
            job_class = get_job(job_name)

            job_result = JobResult.objects.create(
                name=job_class.class_path,
                obj_type=self.job_content_type,
                user=None,
                job_id=uuid.uuid4(),
            )

            # Serialize the file to FileProxy
            data = {"file": self.dummy_file}
            form = job_class().as_form(files=data)
            self.assertTrue(form.is_valid())
            serialized_data = job_class.serialize_data(form.cleaned_data)

            # Assert that the file was serialized to a FileProxy
            self.assertTrue(isinstance(serialized_data["file"], uuid.UUID))
            self.assertEqual(serialized_data["file"], FileProxy.objects.latest().pk)
            self.assertEqual(FileProxy.objects.count(), 1)

            # Run the job
            run_job(data=serialized_data, request=None, commit=False, job_result_pk=job_result.pk)
            job_result.refresh_from_db()

            # Can't check log objects when jobs are reverted (within tests anyways.)
            # This is due to the fake job_logs db not being available for tests.

            # Assert that FileProxy was cleaned up
            self.assertEqual(FileProxy.objects.count(), 0)
Пример #7
0
    def test_run(self):
        with self.settings(JOBS_ROOT=os.path.join(settings.BASE_DIR, "extras/tests/dummy_jobs")):
            self.clear_worker()

            job_content_type = ContentType.objects.get(app_label="extras", model="job")
            job_name = "local/test_site_with_custom_field/TestCreateSiteWithCustomField"
            job_class = get_job(job_name)

            job_result = JobResult.objects.create(
                name=job_class.class_path,
                obj_type=job_content_type,
                user=None,
                job_id=uuid.uuid4(),
            )

            # Run the job
            run_job(data={}, request=self.request, commit=True, job_result_pk=job_result.pk)

            self.wait_on_active_tasks()
            job_result.refresh_from_db()

            self.assertEqual(job_result.status, JobResultStatusChoices.STATUS_COMPLETED)

            # Test site with a value for custom_field
            site_1 = Site.objects.filter(slug="test-site-one")
            self.assertEqual(site_1.count(), 1)
            self.assertEqual(CustomField.objects.filter(name="cf1").count(), 1)
            self.assertEqual(site_1[0].cf["cf1"], "some-value")

            # Test site with default value for custom field
            site_2 = Site.objects.filter(slug="test-site-two")
            self.assertEqual(site_2.count(), 1)
            self.assertEqual(site_2[0].cf["cf1"], "-")
Пример #8
0
    def test_required_object_var(self):
        """
        Test that a required Object variable field behaves as expected.
        """
        with self.settings(JOBS_ROOT=os.path.join(settings.BASE_DIR, "extras/tests/dummy_jobs")):
            module = "test_object_var_required"
            name = "TestRequiredObjectVar"
            job_class = get_job(f"local/{module}/{name}")

            # Prepare the job data
            job_result = JobResult.objects.create(
                name=job_class.class_path,
                obj_type=self.job_content_type,
                user=None,
                job_id=uuid.uuid4(),
            )
            data = {"region": None}
            run_job(data=data, request=None, commit=False, job_result_pk=job_result.pk)
            job_result.refresh_from_db()

            # Assert stuff
            self.assertEqual(job_result.status, JobResultStatusChoices.STATUS_ERRORED)
            log_failure = JobLogEntry.objects.filter(
                grouping="initialization", log_level=LogLevelChoices.LOG_FAILURE
            ).first()
            self.assertIn("region is a required field", log_failure.message)
Пример #9
0
    def test_job_data_as_string(self):
        """
        Test that job doesn't error when not a dictionary.
        """
        with self.settings(JOBS_ROOT=os.path.join(settings.BASE_DIR, "extras/tests/dummy_jobs")):
            module = "test_object_vars"
            name = "TestObjectVars"
            job_class = get_job(f"local/{module}/{name}")

            # Prepare the job data
            job_result = JobResult.objects.create(
                name=job_class.class_path,
                obj_type=self.job_content_type,
                user=None,
                job_id=uuid.uuid4(),
            )
            data = "BAD DATA STRING"
            run_job(data=data, request=None, commit=False, job_result_pk=job_result.pk)
            job_result.refresh_from_db()

            # Assert stuff
            self.assertEqual(job_result.status, JobResultStatusChoices.STATUS_ERRORED)
            log_failure = JobLogEntry.objects.filter(
                grouping="initialization", log_level=LogLevelChoices.LOG_FAILURE
            ).first()
            self.assertIn("Data should be a dictionary", log_failure.message)
Пример #10
0
    def test_ip_address_vars(self):
        """
        Test that IPAddress variable fields behave as expected.

        This test case exercises the following types for both IPv4 and IPv6:

        - IPAddressVar
        - IPAddressWithMaskVar
        - IPNetworkVar
        """
        with self.settings(JOBS_ROOT=os.path.join(settings.BASE_DIR,
                                                  "extras/tests/dummy_jobs")):

            module = "test_ipaddress_vars"
            name = "TestIPAddresses"
            job_class = get_job(f"local/{module}/{name}")

            # Fill out the form
            form_data = dict(
                ipv4_address="1.2.3.4",
                ipv4_with_mask="1.2.3.4/32",
                ipv4_network="1.2.3.0/24",
                ipv6_address="2001:db8::1",
                ipv6_with_mask="2001:db8::1/64",
                ipv6_network="2001:db8::/64",
            )
            form = job_class().as_form(form_data)
            self.assertTrue(form.is_valid())

            # Prepare the job data
            job_result = JobResult.objects.create(
                name=job_class.class_path,
                obj_type=self.job_content_type,
                user=None,
                job_id=uuid.uuid4(),
            )
            data = job_class.serialize_data(form.cleaned_data)

            # Run the job and extract the job payload data
            # Changing commit=True as commit=False will rollback database changes including the
            # logs that we are trying to read. See above note on why we are using the default database.
            # Also need to pass a mock request object as execute_webhooks will be called with the creation
            # of the objects.
            run_job(data=data,
                    request=self.request,
                    commit=True,
                    job_result_pk=job_result.pk)
            job_result.refresh_from_db()

            log_info = JobLogEntry.objects.filter(
                job_result=job_result,
                log_level=LogLevelChoices.LOG_INFO,
                grouping="run").first()

            job_result_data = json.loads(log_info.log_object)

            # Assert stuff
            self.assertEqual(job_result.status,
                             JobResultStatusChoices.STATUS_COMPLETED)
            self.assertEqual(form_data, job_result_data)
Пример #11
0
def run_job_for_testing(job,
                        data=None,
                        commit=True,
                        username="******",
                        request=None):
    """Provide a common interface to run Nautobot jobs as part of unit tests.

    Args:
      job (Job): Job model instance (not Job class) to run
      data (dict): Input data values for any Job variables.
      commit (bool): Whether to commit changes to the database or rollback when done.
      username (str): Username of existing or to-be-created User account to own the JobResult. Ignored if `request.user`
        exists.
      request (HttpRequest): Existing request (if any) to own the JobResult.

    Returns:
      JobResult: representing the executed job
    """
    if data is None:
        data = {}

    # If the request has a user, ignore the username argument and use that user.
    if request and request.user:
        user_instance = request.user
    else:
        User = get_user_model()
        user_instance, _ = User.objects.get_or_create(username=username,
                                                      defaults={
                                                          "is_superuser": True,
                                                          "password":
                                                          "******"
                                                      })
    job_result = JobResult.objects.create(
        name=job.class_path,
        obj_type=get_job_content_type(),
        user=user_instance,
        job_model=job,
        job_id=uuid.uuid4(),
    )

    @contextmanager
    def _web_request_context(user):
        if request:
            yield request
        else:
            yield web_request_context(user=user)

    with _web_request_context(user=user_instance) as request:
        run_job(data=data,
                request=request,
                commit=commit,
                job_result_pk=job_result.pk)
    return job_result
Пример #12
0
    def test_ip_address_vars(self):
        """
        Test that IPAddress variable fields behave as expected.

        This test case exercises the following types for both IPv4 and IPv6:

        - IPAddressVar
        - IPAddressWithMaskVar
        - IPNetworkVar
        """
        with self.settings(JOBS_ROOT=os.path.join(settings.BASE_DIR,
                                                  "extras/tests/dummy_jobs")):

            module = "test_ipaddress_vars"
            name = "TestIPAddresses"
            job_class = get_job(f"local/{module}/{name}")

            # Fill out the form
            form_data = dict(
                ipv4_address="1.2.3.4",
                ipv4_with_mask="1.2.3.4/32",
                ipv4_network="1.2.3.0/24",
                ipv6_address="2001:db8::1",
                ipv6_with_mask="2001:db8::1/64",
                ipv6_network="2001:db8::/64",
            )
            form = job_class().as_form(form_data)
            self.assertTrue(form.is_valid())

            # Prepare the job data
            job_result = JobResult.objects.create(
                name=job_class.class_path,
                obj_type=self.job_content_type,
                user=None,
                job_id=uuid.uuid4(),
            )
            data = job_class.serialize_data(form.cleaned_data)

            # Run the job and extract the job payload data
            run_job(data=data,
                    request=None,
                    commit=False,
                    job_result_pk=job_result.pk)
            job_result.refresh_from_db()
            job_payload = job_result.data["run"]["log"][0][
                2]  # Indexing makes me sad.
            job_result_data = json.loads(job_payload)

            # Assert stuff
            self.assertEqual(job_result.status,
                             JobResultStatusChoices.STATUS_COMPLETED)
            self.assertEqual(form_data, job_result_data)
Пример #13
0
    def test_object_vars(self):
        """
        Test that Object variable fields behave as expected.
        """
        with self.settings(JOBS_ROOT=os.path.join(settings.BASE_DIR,
                                                  "extras/tests/dummy_jobs")):

            module = "test_object_vars"
            name = "TestObjectVars"
            job_class = get_job(f"local/{module}/{name}")

            d = DeviceRole.objects.create(name="role", slug="role")

            # Prepare the job data
            job_result = JobResult.objects.create(
                name=job_class.class_path,
                obj_type=self.job_content_type,
                user=None,
                job_id=uuid.uuid4(),
            )
            data = {
                "role": {
                    "name": "role"
                },
                "roles": [d.pk],
            }

            # Run the job and extract the job payload data
            # See test_ip_address_vars as to why we are changing commit=True and request=self.request.
            run_job(data=data,
                    request=self.request,
                    commit=True,
                    job_result_pk=job_result.pk)
            job_result.refresh_from_db()
            # Test storing additional data in job
            job_result_data = job_result.data["object_vars"]

            info_log = JobLogEntry.objects.filter(
                job_result=job_result,
                log_level=LogLevelChoices.LOG_INFO,
                grouping="run").first()

            # Assert stuff
            self.assertEqual(job_result.status,
                             JobResultStatusChoices.STATUS_COMPLETED)
            self.assertEqual({
                "role": str(d.pk),
                "roles": [str(d.pk)]
            }, job_result_data)
            self.assertEqual(info_log.log_object, "Role: role")
            self.assertEqual(job_result.data["output"], "\nNice Roles, bro.")
Пример #14
0
    def test_run_job_pass(self):
        """Test that file upload succeeds; job SUCCEEDS; and files are deleted."""
        with self.settings(JOBS_ROOT=os.path.join(settings.BASE_DIR,
                                                  "extras/tests/dummy_jobs")):
            job_name = "local/test_file_upload_pass/TestFileUploadPass"
            job_class = get_job(job_name)

            job_result = JobResult.objects.create(
                name=job_class.class_path,
                obj_type=self.job_content_type,
                user=None,
                job_id=uuid.uuid4(),
            )

            # Serialize the file to FileProxy
            data = {"file": self.dummy_file}
            form = job_class().as_form(files=data)
            self.assertTrue(form.is_valid())
            serialized_data = job_class.serialize_data(form.cleaned_data)

            # Assert that the file was serialized to a FileProxy
            self.assertTrue(isinstance(serialized_data["file"], uuid.UUID))
            self.assertEqual(serialized_data["file"],
                             FileProxy.objects.latest().pk)
            self.assertEqual(FileProxy.objects.count(), 1)

            # Run the job
            # See test_ip_address_vars as to why we are changing commit=True and request=self.request.
            run_job(data=serialized_data,
                    request=self.request,
                    commit=True,
                    job_result_pk=job_result.pk)
            job_result.refresh_from_db()

            warning_log = JobLogEntry.objects.filter(
                job_result=job_result,
                log_level=LogLevelChoices.LOG_WARNING,
                grouping="run").first()

            # Assert that file contents were correctly read
            self.assertEqual(
                warning_log.message,
                f"File contents: {self.file_contents}")  # "File contents: ..."

            # Assert that FileProxy was cleaned up
            self.assertEqual(FileProxy.objects.count(), 0)
Пример #15
0
    def test_run_job_fail(self):
        """Test that file upload succeeds; job FAILS; files deleted."""
        with self.settings(JOBS_ROOT=os.path.join(settings.BASE_DIR,
                                                  "extras/tests/dummy_jobs")):
            job_name = "local/test_file_upload_fail/TestFileUploadFail"
            job_class = get_job(job_name)

            job_result = JobResult.objects.create(
                name=job_class.class_path,
                obj_type=self.job_content_type,
                user=None,
                job_id=uuid.uuid4(),
            )

            # Serialize the file to FileProxy
            data = {"file": self.dummy_file}
            form = job_class().as_form(files=data)
            self.assertTrue(form.is_valid())
            serialized_data = job_class.serialize_data(form.cleaned_data)

            # Assert that the file was serialized to a FileProxy
            self.assertTrue(isinstance(serialized_data["file"], uuid.UUID))
            self.assertEqual(serialized_data["file"],
                             FileProxy.objects.latest().pk)
            self.assertEqual(FileProxy.objects.count(), 1)

            # Run the job
            run_job(data=serialized_data,
                    request=None,
                    commit=False,
                    job_result_pk=job_result.pk)
            job_result.refresh_from_db()

            # Assert that file contents were correctly read
            self.assertEqual(
                job_result.data["run"]["log"][0][2],
                f"File contents: {self.file_contents}"  # "File contents: ..."
            )
            # Also ensure the standard log message about aborting the transaction is present
            self.assertEqual(
                job_result.data["run"]["log"][-1][-1],
                "Database changes have been reverted due to error.")

            # Assert that FileProxy was cleaned up
            self.assertEqual(FileProxy.objects.count(), 0)
Пример #16
0
    def test_object_vars(self):
        """
        Test that Object variable fields behave as expected.
        """
        with self.settings(JOBS_ROOT=os.path.join(settings.BASE_DIR,
                                                  "extras/tests/dummy_jobs")):

            module = "test_object_vars"
            name = "TestObjectVars"
            job_class = get_job(f"local/{module}/{name}")

            d = DeviceRole.objects.create(name="role", slug="role")

            # Prepare the job data
            job_result = JobResult.objects.create(
                name=job_class.class_path,
                obj_type=self.job_content_type,
                user=None,
                job_id=uuid.uuid4(),
            )
            data = {
                "role": {
                    "name": "role"
                },
                "roles": [d.pk],
            }

            # Run the job and extract the job payload data
            run_job(data=data,
                    request=None,
                    commit=False,
                    job_result_pk=job_result.pk)
            job_result.refresh_from_db()
            job_payload = job_result.data["run"]["log"][0][
                2]  # Indexing makes me sad.
            job_result_data = json.loads(job_payload)

            # Assert stuff
            self.assertEqual(job_result.status,
                             JobResultStatusChoices.STATUS_COMPLETED)
            self.assertEqual({
                "role": str(d.pk),
                "roles": [str(d.pk)]
            }, job_result_data)
Пример #17
0
    def test_run_job_pass(self):
        """Test that file upload succeeds; job SUCCEEDS; and files are deleted."""
        with self.settings(JOBS_ROOT=os.path.join(settings.BASE_DIR,
                                                  "extras/tests/dummy_jobs")):
            job_name = "local/test_file_upload_pass/TestFileUploadPass"
            job_class = get_job(job_name)

            job_result = JobResult.objects.create(
                name=job_class.class_path,
                obj_type=self.job_content_type,
                user=None,
                job_id=uuid.uuid4(),
            )

            # Serialize the file to FileProxy
            data = {"file": self.dummy_file}
            form = job_class().as_form(files=data)
            self.assertTrue(form.is_valid())
            serialized_data = job_class.serialize_data(form.cleaned_data)

            # Assert that the file was serialized to a FileProxy
            self.assertTrue(isinstance(serialized_data["file"], uuid.UUID))
            self.assertEqual(serialized_data["file"],
                             FileProxy.objects.latest().pk)
            self.assertEqual(FileProxy.objects.count(), 1)

            # Run the job
            run_job(data=serialized_data,
                    request=None,
                    commit=False,
                    job_result_pk=job_result.pk)
            job_result.refresh_from_db()

            # Assert that file contents were correctly read
            self.assertEqual(
                job_result.data["run"]["log"][0][2],
                f"File contents: {self.file_contents}"  # "File contents: ..."
            )

            # Assert that FileProxy was cleaned up
            self.assertEqual(FileProxy.objects.count(), 0)