diff --git a/moto/batch/models.py b/moto/batch/models.py index cafb5db1c..0af0677b1 100644 --- a/moto/batch/models.py +++ b/moto/batch/models.py @@ -1585,22 +1585,34 @@ class BatchBackend(BaseBackend): return jobs def cancel_job(self, job_id, reason): + if job_id == "": + raise ClientException( + "'reason' is a required field (cannot be an empty string)" + ) + if reason == "": + raise ClientException( + "'jobId' is a required field (cannot be an empty string)" + ) + job = self.get_job_by_id(job_id) - if job.status in ["SUBMITTED", "PENDING", "RUNNABLE"]: - job.terminate(reason) - # No-Op for jobs that have already started - user has to explicitly terminate those + if job is not None: + if job.status in ["SUBMITTED", "PENDING", "RUNNABLE"]: + job.terminate(reason) + # No-Op for jobs that have already started - user has to explicitly terminate those def terminate_job(self, job_id, reason): - if job_id is None: - raise ClientException("Job ID does not exist") - if reason is None: - raise ClientException("Reason does not exist") + if job_id == "": + raise ClientException( + "'reason' is a required field (cannot be a empty string)" + ) + if reason == "": + raise ClientException( + "'jobId' is a required field (cannot be a empty string)" + ) job = self.get_job_by_id(job_id) - if job is None: - raise ClientException("Job not found") - - job.terminate(reason) + if job is not None: + job.terminate(reason) def tag_resource(self, resource_arn, tags): tags = self.tagger.convert_dict_to_tags_input(tags or {}) diff --git a/tests/test_batch/test_batch_jobs.py b/tests/test_batch/test_batch_jobs.py index 95cdda6eb..b6178db38 100644 --- a/tests/test_batch/test_batch_jobs.py +++ b/tests/test_batch/test_batch_jobs.py @@ -3,6 +3,7 @@ from . import _get_clients, _setup import datetime import sure # noqa # pylint: disable=unused-import from moto import mock_batch, mock_iam, mock_ec2, mock_ecs, mock_logs +import botocore.exceptions import pytest import time from uuid import uuid4 @@ -245,6 +246,33 @@ def test_terminate_job(): resp["events"][0]["message"].should.equal("start") +@mock_batch +def test_terminate_nonexisting_job(): + """ + Test verifies that you get a 200 HTTP status code when terminating a non-existing job. + """ + _, _, _, _, batch_client = _get_clients() + resp = batch_client.terminate_job( + jobId="nonexisting_job", reason="test_terminate_nonexisting_job" + ) + resp["ResponseMetadata"]["HTTPStatusCode"].should.equal(200) + + +@mock_batch +def test_terminate_job_empty_argument_strings(): + """ + Test verifies that a `ClientException` is raised if `jobId` or `reason` is a empty string when terminating a job. + """ + _, _, _, _, batch_client = _get_clients() + with pytest.raises(botocore.exceptions.ClientError) as exc: + batch_client.terminate_job(jobId="", reason="not_a_empty_string") + assert exc.match("ClientException") + + with pytest.raises(botocore.exceptions.ClientError) as exc: + batch_client.terminate_job(jobId="not_a_empty_string", reason="") + assert exc.match("ClientException") + + @mock_logs @mock_ec2 @mock_ecs @@ -315,6 +343,33 @@ def test_cancel_running_job(): resp["jobs"][0].shouldnt.have.key("statusReason") +@mock_batch +def test_cancel_nonexisting_job(): + """ + Test verifies that you get a 200 HTTP status code when cancelling a non-existing job. + """ + _, _, _, _, batch_client = _get_clients() + resp = batch_client.cancel_job( + jobId="nonexisting_job", reason="test_cancel_nonexisting_job" + ) + resp["ResponseMetadata"]["HTTPStatusCode"].should.equal(200) + + +@mock_batch +def test_cancel_job_empty_argument_strings(): + """ + Test verifies that a `ClientException` is raised if `jobId` or `reason` is a empty string when cancelling a job. + """ + _, _, _, _, batch_client = _get_clients() + with pytest.raises(botocore.exceptions.ClientError) as exc: + batch_client.cancel_job(jobId="", reason="not_a_empty_string") + assert exc.match("ClientException") + + with pytest.raises(botocore.exceptions.ClientError) as exc: + batch_client.cancel_job(jobId="not_a_empty_string", reason="") + assert exc.match("ClientException") + + def _wait_for_job_status(client, job_id, status, seconds_to_wait=30): _wait_for_job_statuses(client, job_id, [status], seconds_to_wait)