From 3a249771fbf6eaad12ddc87923edaab2881dfac5 Mon Sep 17 00:00:00 2001 From: Mitchell Hynes Date: Thu, 17 Feb 2022 15:30:48 -0330 Subject: [PATCH 1/6] Initial commit for textract --- moto/__init__.py | 1 + moto/backend_index.py | 1 + moto/textract/__init__.py | 5 +++ moto/textract/exceptions.py | 30 +++++++++++++ moto/textract/models.py | 66 ++++++++++++++++++++++++++++ moto/textract/responses.py | 45 +++++++++++++++++++ moto/textract/urls.py | 12 +++++ tests/test_textract/__init__.py | 0 tests/test_textract/test_server.py | 13 ++++++ tests/test_textract/test_textract.py | 31 +++++++++++++ 10 files changed, 204 insertions(+) create mode 100644 moto/textract/__init__.py create mode 100644 moto/textract/exceptions.py create mode 100644 moto/textract/models.py create mode 100644 moto/textract/responses.py create mode 100644 moto/textract/urls.py create mode 100644 tests/test_textract/__init__.py create mode 100644 tests/test_textract/test_server.py create mode 100644 tests/test_textract/test_textract.py diff --git a/moto/__init__.py b/moto/__init__.py index 00036dbaa..94ff40c7c 100644 --- a/moto/__init__.py +++ b/moto/__init__.py @@ -149,6 +149,7 @@ XRaySegment = lazy_load(".xray", "XRaySegment") mock_xray = lazy_load(".xray", "mock_xray") mock_xray_client = lazy_load(".xray", "mock_xray_client") mock_wafv2 = lazy_load(".wafv2", "mock_wafv2") +mock_textract = lazy_load(".textract", "mock_textract") class MockAll(ContextDecorator): diff --git a/moto/backend_index.py b/moto/backend_index.py index 7a60336e3..23c94b52c 100644 --- a/moto/backend_index.py +++ b/moto/backend_index.py @@ -148,6 +148,7 @@ backend_url_patterns = [ re.compile("https?://ingest\\.timestream\\.(.+)\\.amazonaws\\.com/"), ), ("transcribe", re.compile("https?://transcribe\\.(.+)\\.amazonaws\\.com")), + ("textract", re.compile("https?://textract\\.(.+)\\.amazonaws\\.com")), ("wafv2", re.compile("https?://wafv2\\.(.+)\\.amazonaws.com")), ("xray", re.compile("https?://xray\\.(.+)\\.amazonaws.com")), ] diff --git a/moto/textract/__init__.py b/moto/textract/__init__.py new file mode 100644 index 000000000..f695bec0b --- /dev/null +++ b/moto/textract/__init__.py @@ -0,0 +1,5 @@ +"""textract module initialization; sets value for base decorator.""" +from .models import textract_backends +from ..core.models import base_decorator + +mock_textract = base_decorator(textract_backends) \ No newline at end of file diff --git a/moto/textract/exceptions.py b/moto/textract/exceptions.py new file mode 100644 index 000000000..f0df6b185 --- /dev/null +++ b/moto/textract/exceptions.py @@ -0,0 +1,30 @@ +"""Exceptions raised by the textract service.""" +from moto.core.exceptions import JsonRESTError + +class InvalidJobIdException(JsonRESTError): + code = 400 + + def __init__(self): + super().__init__( + __class__.__name__, + "An invalid job identifier was passed.", + ) + +class InvalidS3ObjectException(JsonRESTError): + code = 400 + + def __init__(self): + super().__init__( + __class__.__name__, + "Amazon Textract is unable to access the S3 object that's specified in the request.", + ) + +class InvalidParameterException(JsonRESTError): + code = 400 + + def __init__(self): + super().__init__( + __class__.__name__, + "An input parameter violated a constraint. For example, in synchronous operations, an InvalidParameterException exception occurs when neither of the S3Object or Bytes values are supplied in the Document request parameter. Validate your parameter before calling the API operation again.", + ) + diff --git a/moto/textract/models.py b/moto/textract/models.py new file mode 100644 index 000000000..50cada95a --- /dev/null +++ b/moto/textract/models.py @@ -0,0 +1,66 @@ +"""TextractBackend class with methods for supported APIs.""" + +import uuid +from random import randint +from collections import defaultdict + +from moto.core import BaseBackend, BaseModel +from moto.core.utils import BackendDict + +from .exceptions import InvalidParameterException, InvalidJobIdException + +class TextractJobStatus: + in_progress = "IN_PROGRESS" + succeeded = "SUCCEEDED" + failed = "FAILED" + partial_success = "PARTIAL_SUCCESS" + +class TextractJob(BaseModel): + def __init__(self, job): + self.job = job + +class TextractBackend(BaseBackend): + """Implementation of Textract APIs.""" + JOB_STATUS = TextractJobStatus.succeeded + PAGES = {"Pages": randint(5, 500)} + BLOCKS = [] + + def __init__(self, region_name=None): + self.region_name = region_name + self.async_text_detection_jobs = defaultdict() + + def reset(self): + """Re-initialize all attributes for this instance.""" + region_name = self.region_name + self.async_text_detection_jobs = defaultdict() + self.__dict__ = {} + self.__init__(region_name) + + def get_document_text_detection(self, job_id, max_results, next_token): + job = self.async_text_detection_jobs.get(job_id) + if not job: + raise InvalidJobIdException() + return job + + def start_document_text_detection( + self, + document_location, + client_request_token, + job_tag, + notification_channel, + output_config, + kms_key_id, + ): + if not document_location: + raise InvalidParameterException() + job_id = uuid.uuid4() + self.async_text_detection_jobs[job_id] = TextractJob({ + "Blocks": TextractBackend.BLOCKS, + "DetectDocumentTextModelVersion": "1.0", + "DocumentMetadata": {"Pages": TextractBackend.PAGES}, + "JobStatus": TextractBackend.JOB_STATUS, + }) + return job_id + + +textract_backends = BackendDict(TextractBackend, "textract") diff --git a/moto/textract/responses.py b/moto/textract/responses.py new file mode 100644 index 000000000..712b755e8 --- /dev/null +++ b/moto/textract/responses.py @@ -0,0 +1,45 @@ +"""Handles incoming textract requests, invokes methods, returns responses.""" +import json + +from moto.core.responses import BaseResponse +from .models import textract_backends + + +class TextractResponse(BaseResponse): + """Handler for Textract requests and responses.""" + + @property + def textract_backend(self): + """Return backend instance specific for this region.""" + return textract_backends[self.region] + + def get_document_text_detection(self): + params = self._get_params() + job_id = params.get("JobId") + max_results = params.get("MaxResults") + next_token = params.get("NextToken") + job = self.textract_backend.get_document_text_detection( + job_id=job_id, + max_results=max_results, + next_token=next_token, + ) + return json.dumps(job) + + + def start_document_text_detection(self): + params = self._get_params() + document_location = params.get("DocumentLocation") + client_request_token = params.get("ClientRequestToken") + job_tag = params.get("JobTag") + notification_channel = params.get("NotificationChannel") + output_config = params.get("OutputConfig") + kms_key_id = params.get("KMSKeyId") + job_id = self.textract_backend.start_document_text_detection( + document_location=document_location, + client_request_token=client_request_token, + job_tag=job_tag, + notification_channel=notification_channel, + output_config=output_config, + kms_key_id=kms_key_id, + ) + return json.dumps(dict(JobId=job_id)) diff --git a/moto/textract/urls.py b/moto/textract/urls.py new file mode 100644 index 000000000..2f8e7e12f --- /dev/null +++ b/moto/textract/urls.py @@ -0,0 +1,12 @@ +"""textract base URL and path.""" +from .responses import TextractResponse + +url_bases = [ + r"https?://textract\.(.+)\.amazonaws\.com", +] + + + +url_paths = { + "{0}/$": TextractResponse.dispatch, +} diff --git a/tests/test_textract/__init__.py b/tests/test_textract/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/test_textract/test_server.py b/tests/test_textract/test_server.py new file mode 100644 index 000000000..9bfea0613 --- /dev/null +++ b/tests/test_textract/test_server.py @@ -0,0 +1,13 @@ +"""Test different server responses.""" +import sure # noqa # pylint: disable=unused-import + +import moto.server as server + + +def test_textract_list(): + backend = server.create_backend_app("textract") + test_client = backend.test_client() + + resp = test_client.get("/") + resp.status_code.should.equal(200) + str(resp.data).should.contain("?") \ No newline at end of file diff --git a/tests/test_textract/test_textract.py b/tests/test_textract/test_textract.py new file mode 100644 index 000000000..86746f65d --- /dev/null +++ b/tests/test_textract/test_textract.py @@ -0,0 +1,31 @@ +"""Unit tests for textract-supported APIs.""" +import boto3 + +from moto import mock_textract + +# See our Development Tips on writing tests for hints on how to write good tests: +# http://docs.getmoto.org/en/latest/docs/contributing/development_tips/tests.html + + + +# @mock_textract +# def test_get_document_text_detection(): +# client = boto3.client("textract", region_name="us-east-1") +# resp = client.get_document_text_detection() + +# raise Exception("NotYetImplemented") + + +@mock_textract +def test_start_document_text_detection(): + client = boto3.client("textract", region_name="us-east-2") + resp = client.start_document_text_detection( + DocumentLocation={ + 'S3Object': { + 'Bucket': 'bucket', + 'Name': 'name', + } + }, + ) + + raise Exception("NotYetImplemented") From 0776ae1f526f0c7046d591660d6947312cc87d01 Mon Sep 17 00:00:00 2001 From: Mitchell Hynes Date: Thu, 17 Feb 2022 16:01:40 -0330 Subject: [PATCH 2/6] Ran the URL script --- moto/backend_index.py | 2 +- tests/test_textract/test_textract.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/moto/backend_index.py b/moto/backend_index.py index 23c94b52c..f748157be 100644 --- a/moto/backend_index.py +++ b/moto/backend_index.py @@ -139,6 +139,7 @@ backend_url_patterns = [ ("sts", re.compile("https?://sts\\.(.*\\.)?amazonaws\\.com")), ("support", re.compile("https?://support\\.(.+)\\.amazonaws\\.com")), ("swf", re.compile("https?://swf\\.(.+)\\.amazonaws\\.com")), + ("textract", re.compile("https?://textract\\.(.+)\\.amazonaws\\.com")), ( "timestream-write", re.compile("https?://ingest\\.timestream\\.(.+)\\.amazonaws\\.com"), @@ -148,7 +149,6 @@ backend_url_patterns = [ re.compile("https?://ingest\\.timestream\\.(.+)\\.amazonaws\\.com/"), ), ("transcribe", re.compile("https?://transcribe\\.(.+)\\.amazonaws\\.com")), - ("textract", re.compile("https?://textract\\.(.+)\\.amazonaws\\.com")), ("wafv2", re.compile("https?://wafv2\\.(.+)\\.amazonaws.com")), ("xray", re.compile("https?://xray\\.(.+)\\.amazonaws.com")), ] diff --git a/tests/test_textract/test_textract.py b/tests/test_textract/test_textract.py index 86746f65d..bac56cdad 100644 --- a/tests/test_textract/test_textract.py +++ b/tests/test_textract/test_textract.py @@ -18,7 +18,7 @@ from moto import mock_textract @mock_textract def test_start_document_text_detection(): - client = boto3.client("textract", region_name="us-east-2") + client = boto3.client("textract", region_name="us-east-1") resp = client.start_document_text_detection( DocumentLocation={ 'S3Object': { From 64c16c715d777a51df747ab3b59e4c00cc0148d1 Mon Sep 17 00:00:00 2001 From: Bert Blommers Date: Fri, 18 Feb 2022 18:49:54 -0100 Subject: [PATCH 3/6] Textract - request parameters are part of the body --- moto/textract/models.py | 2 +- moto/textract/responses.py | 2 +- tests/test_textract/test_textract.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/moto/textract/models.py b/moto/textract/models.py index 50cada95a..1668a7c3c 100644 --- a/moto/textract/models.py +++ b/moto/textract/models.py @@ -53,7 +53,7 @@ class TextractBackend(BaseBackend): ): if not document_location: raise InvalidParameterException() - job_id = uuid.uuid4() + job_id = str(uuid.uuid4()) self.async_text_detection_jobs[job_id] = TextractJob({ "Blocks": TextractBackend.BLOCKS, "DetectDocumentTextModelVersion": "1.0", diff --git a/moto/textract/responses.py b/moto/textract/responses.py index 712b755e8..f214659a6 100644 --- a/moto/textract/responses.py +++ b/moto/textract/responses.py @@ -27,7 +27,7 @@ class TextractResponse(BaseResponse): def start_document_text_detection(self): - params = self._get_params() + params = json.loads(self.body) document_location = params.get("DocumentLocation") client_request_token = params.get("ClientRequestToken") job_tag = params.get("JobTag") diff --git a/tests/test_textract/test_textract.py b/tests/test_textract/test_textract.py index bac56cdad..a5e739a7e 100644 --- a/tests/test_textract/test_textract.py +++ b/tests/test_textract/test_textract.py @@ -28,4 +28,4 @@ def test_start_document_text_detection(): }, ) - raise Exception("NotYetImplemented") + resp.should.have.key("JobId") From 7dc5adb5d5806d16bc4683c045a7546a05570e35 Mon Sep 17 00:00:00 2001 From: Mitchell Hynes Date: Mon, 21 Feb 2022 11:43:36 -0330 Subject: [PATCH 4/6] Textract: Addressed comments --- docs/docs/services/textract.rst | 45 +++++++++++++++++ moto/textract/__init__.py | 2 +- moto/textract/exceptions.py | 7 +-- moto/textract/models.py | 21 +++++--- moto/textract/responses.py | 9 ++-- moto/textract/urls.py | 2 - tests/test_textract/test_server.py | 74 ++++++++++++++++++++++++++-- tests/test_textract/test_textract.py | 72 ++++++++++++++++++++++----- 8 files changed, 200 insertions(+), 32 deletions(-) create mode 100644 docs/docs/services/textract.rst diff --git a/docs/docs/services/textract.rst b/docs/docs/services/textract.rst new file mode 100644 index 000000000..398f08173 --- /dev/null +++ b/docs/docs/services/textract.rst @@ -0,0 +1,45 @@ +.. _implementedservice_textract: + +.. |start-h3| raw:: html + +

+ +.. |end-h3| raw:: html + +

+ +========== +textract +========== + +.. autoclass:: moto.textract.models.TextractBackend + +|start-h3| Example usage |end-h3| + +.. sourcecode:: python + + @mock_textract + def test_textract_behaviour: + boto3.client("textract") + ... + + + +|start-h3| Implemented features for this service |end-h3| + + - [ ] analyze_document + - [ ] analyze_expense + - [ ] analyze_id + - [ ] can_paginate + - [ ] detect_document_text + - [ ] get_document_analysis + - [X] get_document_text_detection + - [ ] get_expense_analysis + - [ ] get_paginator + - [ ] get_waiter + - [ ] start_document_analysis + - [X] start_document_text_detection + - [ ] start_expense_analysis + + Pagination has not yet been implemented + \ No newline at end of file diff --git a/moto/textract/__init__.py b/moto/textract/__init__.py index f695bec0b..4bce9832b 100644 --- a/moto/textract/__init__.py +++ b/moto/textract/__init__.py @@ -2,4 +2,4 @@ from .models import textract_backends from ..core.models import base_decorator -mock_textract = base_decorator(textract_backends) \ No newline at end of file +mock_textract = base_decorator(textract_backends) diff --git a/moto/textract/exceptions.py b/moto/textract/exceptions.py index f0df6b185..b2b25f72b 100644 --- a/moto/textract/exceptions.py +++ b/moto/textract/exceptions.py @@ -1,15 +1,16 @@ """Exceptions raised by the textract service.""" from moto.core.exceptions import JsonRESTError + class InvalidJobIdException(JsonRESTError): code = 400 def __init__(self): super().__init__( - __class__.__name__, - "An invalid job identifier was passed.", + __class__.__name__, "An invalid job identifier was passed.", ) + class InvalidS3ObjectException(JsonRESTError): code = 400 @@ -19,6 +20,7 @@ class InvalidS3ObjectException(JsonRESTError): "Amazon Textract is unable to access the S3 object that's specified in the request.", ) + class InvalidParameterException(JsonRESTError): code = 400 @@ -27,4 +29,3 @@ class InvalidParameterException(JsonRESTError): __class__.__name__, "An input parameter violated a constraint. For example, in synchronous operations, an InvalidParameterException exception occurs when neither of the S3Object or Bytes values are supplied in the Document request parameter. Validate your parameter before calling the API operation again.", ) - diff --git a/moto/textract/models.py b/moto/textract/models.py index 1668a7c3c..64eebd324 100644 --- a/moto/textract/models.py +++ b/moto/textract/models.py @@ -9,18 +9,25 @@ from moto.core.utils import BackendDict from .exceptions import InvalidParameterException, InvalidJobIdException + class TextractJobStatus: in_progress = "IN_PROGRESS" succeeded = "SUCCEEDED" failed = "FAILED" partial_success = "PARTIAL_SUCCESS" + class TextractJob(BaseModel): def __init__(self, job): self.job = job + def to_dict(self): + return self.job + + class TextractBackend(BaseBackend): """Implementation of Textract APIs.""" + JOB_STATUS = TextractJobStatus.succeeded PAGES = {"Pages": randint(5, 500)} BLOCKS = [] @@ -54,12 +61,14 @@ class TextractBackend(BaseBackend): if not document_location: raise InvalidParameterException() job_id = str(uuid.uuid4()) - self.async_text_detection_jobs[job_id] = TextractJob({ - "Blocks": TextractBackend.BLOCKS, - "DetectDocumentTextModelVersion": "1.0", - "DocumentMetadata": {"Pages": TextractBackend.PAGES}, - "JobStatus": TextractBackend.JOB_STATUS, - }) + self.async_text_detection_jobs[job_id] = TextractJob( + { + "Blocks": TextractBackend.BLOCKS, + "DetectDocumentTextModelVersion": "1.0", + "DocumentMetadata": {"Pages": TextractBackend.PAGES}, + "JobStatus": TextractBackend.JOB_STATUS, + } + ) return job_id diff --git a/moto/textract/responses.py b/moto/textract/responses.py index f214659a6..ab91945db 100644 --- a/moto/textract/responses.py +++ b/moto/textract/responses.py @@ -14,18 +14,15 @@ class TextractResponse(BaseResponse): return textract_backends[self.region] def get_document_text_detection(self): - params = self._get_params() + params = json.loads(self.body) job_id = params.get("JobId") max_results = params.get("MaxResults") next_token = params.get("NextToken") job = self.textract_backend.get_document_text_detection( - job_id=job_id, - max_results=max_results, - next_token=next_token, - ) + job_id=job_id, max_results=max_results, next_token=next_token, + ).to_dict() return json.dumps(job) - def start_document_text_detection(self): params = json.loads(self.body) document_location = params.get("DocumentLocation") diff --git a/moto/textract/urls.py b/moto/textract/urls.py index 2f8e7e12f..d5b9cf74f 100644 --- a/moto/textract/urls.py +++ b/moto/textract/urls.py @@ -5,8 +5,6 @@ url_bases = [ r"https?://textract\.(.+)\.amazonaws\.com", ] - - url_paths = { "{0}/$": TextractResponse.dispatch, } diff --git a/tests/test_textract/test_server.py b/tests/test_textract/test_server.py index 9bfea0613..26f7d02b5 100644 --- a/tests/test_textract/test_server.py +++ b/tests/test_textract/test_server.py @@ -1,13 +1,81 @@ """Test different server responses.""" import sure # noqa # pylint: disable=unused-import +import json import moto.server as server +from moto import mock_textract -def test_textract_list(): +@mock_textract +def test_textract_start_text_detection(): backend = server.create_backend_app("textract") test_client = backend.test_client() - resp = test_client.get("/") + headers = {"X-Amz-Target": "X-Amz-Target=Textract.StartDocumentTextDetection"} + request_body = { + "DocumentLocation": { + "S3Object": {"Bucket": "bucket", "Name": "name", "Version": "version",} + } + } + + resp = test_client.post("/", headers=headers, json=request_body) + data = json.loads(resp.data.decode("utf-8")) resp.status_code.should.equal(200) - str(resp.data).should.contain("?") \ No newline at end of file + data["JobId"].should.be.an(str) + + +@mock_textract +def test_textract_start_text_detection_without_document_location(): + backend = server.create_backend_app("textract") + test_client = backend.test_client() + + headers = {"X-Amz-Target": "X-Amz-Target=Textract.StartDocumentTextDetection"} + resp = test_client.post("/", headers=headers, json={}) + data = json.loads(resp.data.decode("utf-8")) + resp.status_code.should.equal(400) + data["__type"].should.equal("InvalidParameterException") + data["message"].should.equal( + "An input parameter violated a constraint. For example, in synchronous operations, an InvalidParameterException exception occurs when neither of the S3Object or Bytes values are supplied in the Document request parameter. Validate your parameter before calling the API operation again." + ) + + +@mock_textract +def test_textract_get_text_detection(): + backend = server.create_backend_app("textract") + test_client = backend.test_client() + + headers = {"X-Amz-Target": "X-Amz-Target=Textract.StartDocumentTextDetection"} + request_body = { + "DocumentLocation": { + "S3Object": {"Bucket": "bucket", "Name": "name", "Version": "version",} + } + } + + resp = test_client.post("/", headers=headers, json=request_body) + start_job_data = json.loads(resp.data.decode("utf-8")) + + headers = {"X-Amz-Target": "X-Amz-Target=Textract.GetDocumentTextDetection"} + request_body = { + "JobId": start_job_data["JobId"], + } + resp = test_client.post("/", headers=headers, json=request_body) + resp.status_code.should.equal(200) + data = json.loads(resp.data.decode("utf-8")) + data["JobStatus"].should.equal("SUCCEEDED") + + +@mock_textract +def test_textract_get_text_detection_without_job_id(): + backend = server.create_backend_app("textract") + test_client = backend.test_client() + + headers = {"X-Amz-Target": "X-Amz-Target=Textract.GetDocumentTextDetection"} + request_body = { + "JobId": "invalid_job_id", + } + resp = test_client.post("/", headers=headers, json=request_body) + resp.status_code.should.equal(400) + data = json.loads(resp.data.decode("utf-8")) + print(data) + data["__type"].should.equal("InvalidJobIdException") + data["message"].should.equal("An invalid job identifier was passed.") diff --git a/tests/test_textract/test_textract.py b/tests/test_textract/test_textract.py index a5e739a7e..96b6bf2ea 100644 --- a/tests/test_textract/test_textract.py +++ b/tests/test_textract/test_textract.py @@ -1,31 +1,81 @@ """Unit tests for textract-supported APIs.""" +from random import randint +from botocore.exceptions import ClientError, ParamValidationError +import pytest import boto3 +from moto.textract.models import TextractBackend from moto import mock_textract # See our Development Tips on writing tests for hints on how to write good tests: # http://docs.getmoto.org/en/latest/docs/contributing/development_tips/tests.html +@mock_textract +def test_get_document_text_detection(): + TextractBackend.JOB_STATUS = "SUCCEEDED" + TextractBackend.PAGES = randint(5, 500) + TextractBackend.BLOCKS = [ + { + "Text": "This is a test", + "Id": "0", + "Confidence": "100", + "Geometry": { + "BoundingBox": { + "Width": "0.5", + "Height": "0.5", + "Left": "0.5", + "Top": "0.5", + }, + }, + } + ] -# @mock_textract -# def test_get_document_text_detection(): -# client = boto3.client("textract", region_name="us-east-1") -# resp = client.get_document_text_detection() + client = boto3.client("textract", region_name="us-east-1") + job = client.start_document_text_detection( + DocumentLocation={"S3Object": {"Bucket": "bucket", "Name": "name"}}, + ) -# raise Exception("NotYetImplemented") + resp = client.get_document_text_detection(JobId=job["JobId"]) + + resp["Blocks"][0]["Text"].should.equal("This is a test") + resp["Blocks"][0]["Id"].should.equal("0") + resp["Blocks"][0]["Confidence"].should.equal("100") + resp["Blocks"][0]["Geometry"]["BoundingBox"]["Width"].should.equal("0.5") + resp["Blocks"][0]["Geometry"]["BoundingBox"]["Height"].should.equal("0.5") + resp["Blocks"][0]["Geometry"]["BoundingBox"]["Left"].should.equal("0.5") + resp["Blocks"][0]["Geometry"]["BoundingBox"]["Top"].should.equal("0.5") + resp["JobStatus"].should.equal("SUCCEEDED") + resp["DocumentMetadata"]["Pages"].should.equal(TextractBackend.PAGES) @mock_textract def test_start_document_text_detection(): client = boto3.client("textract", region_name="us-east-1") resp = client.start_document_text_detection( - DocumentLocation={ - 'S3Object': { - 'Bucket': 'bucket', - 'Name': 'name', - } - }, + DocumentLocation={"S3Object": {"Bucket": "bucket", "Name": "name",}}, ) resp.should.have.key("JobId") + + +@mock_textract +def test_get_document_text_detection_without_job_id(): + client = boto3.client("textract", region_name="us-east-1") + with pytest.raises(ClientError) as e: + client.get_document_text_detection(JobId="Invalid Job Id") + + e.value.response["Error"]["Code"].should.equal("InvalidJobIdException") + + +@mock_textract +def test_get_document_text_detection_without_document_location(): + client = boto3.client("textract", region_name="us-east-1") + with pytest.raises(ParamValidationError) as e: + client.start_document_text_detection() + + assert e.typename == "ParamValidationError" + assert ( + 'Parameter validation failed:\nMissing required parameter in input: "DocumentLocation"' + in e.value.args + ) From 30ab3f86c1795a8db48e46302bb3549e9e1ac8eb Mon Sep 17 00:00:00 2001 From: Mitchell Hynes Date: Mon, 21 Feb 2022 14:26:50 -0330 Subject: [PATCH 5/6] Fixed test --- tests/test_textract/test_textract.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/test_textract/test_textract.py b/tests/test_textract/test_textract.py index 96b6bf2ea..3d2c49e45 100644 --- a/tests/test_textract/test_textract.py +++ b/tests/test_textract/test_textract.py @@ -4,8 +4,9 @@ from botocore.exceptions import ClientError, ParamValidationError import pytest import boto3 +from unittest import SkipTest from moto.textract.models import TextractBackend -from moto import mock_textract +from moto import settings, mock_textract # See our Development Tips on writing tests for hints on how to write good tests: # http://docs.getmoto.org/en/latest/docs/contributing/development_tips/tests.html @@ -13,6 +14,9 @@ from moto import mock_textract @mock_textract def test_get_document_text_detection(): + if settings.TEST_SERVER_MODE: + raise SkipTest("Cannot set textract backend values in server mode") + TextractBackend.JOB_STATUS = "SUCCEEDED" TextractBackend.PAGES = randint(5, 500) TextractBackend.BLOCKS = [ From c2dc11ddb1d6751523165a74585bfd21666027a6 Mon Sep 17 00:00:00 2001 From: Mitchell Hynes Date: Mon, 21 Feb 2022 15:49:29 -0330 Subject: [PATCH 6/6] Removed print statement --- tests/test_textract/test_server.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_textract/test_server.py b/tests/test_textract/test_server.py index 26f7d02b5..e9b961a73 100644 --- a/tests/test_textract/test_server.py +++ b/tests/test_textract/test_server.py @@ -76,6 +76,5 @@ def test_textract_get_text_detection_without_job_id(): resp = test_client.post("/", headers=headers, json=request_body) resp.status_code.should.equal(400) data = json.loads(resp.data.decode("utf-8")) - print(data) data["__type"].should.equal("InvalidJobIdException") data["message"].should.equal("An invalid job identifier was passed.")