diff --git a/.isort.cfg b/.isort.cfg index 31b301bd369bd123cfc9e83f902161c529de6989..7fa1b117d09b99cf7ce2a459b8f071e9af81dd89 100644 --- a/.isort.cfg +++ b/.isort.cfg @@ -8,4 +8,4 @@ line_length = 88 default_section=FIRSTPARTY known_first_party = arkindex,arkindex_common -known_third_party =PIL,apistar,pytest,requests,setuptools,tenacity +known_third_party =PIL,apistar,pytest,requests,setuptools,tenacity,yaml diff --git a/arkindex_worker/worker.py b/arkindex_worker/worker.py index 8f10b07e17618e6c92910ec252e486ca80f4f66b..c28c1e9065a62bdcc3756f02d3d499b5d3ea7f27 100644 --- a/arkindex_worker/worker.py +++ b/arkindex_worker/worker.py @@ -7,6 +7,7 @@ import sys import uuid from enum import Enum +import yaml from apistar.exceptions import ErrorResponse from arkindex import ArkindexClient, options_from_env @@ -33,16 +34,27 @@ class BaseWorker(object): self.worker_version_id = os.environ.get("WORKER_VERSION_ID") if not self.worker_version_id: - raise Exception( - "Missing WORKER_VERSION_ID environment variable to start the Worker" + logger.warning( + "Missing WORKER_VERSION_ID environment variable, worker is in read-only mode" ) logger.info(f"Worker will use {self.work_dir} as working directory") + @property + def is_read_only(self): + """Worker cannot publish anything without a worker version ID""" + return self.worker_version_id is None + def configure(self): """ Configure worker using cli args and environment variables """ + self.parser.add_argument( + "-c", + "--config", + help="Alternative configuration file when running without a Worker Version ID", + type=open, + ) self.parser.add_argument( "-v", "--verbose", @@ -66,6 +78,25 @@ class BaseWorker(object): self.api_client = ArkindexClient(**options_from_env()) logger.debug(f"Setup Arkindex API client on {self.api_client.document.url}") + if self.worker_version_id: + # Retrieve initial configuration from API + worker_version = self.api_client.request( + "RetrieveWorkerVersion", id=self.worker_version_id + ) + logger.info( + f"Loaded worker {worker_version['worker']['name']} revision {worker_version['revision']} from API" + ) + self.config = worker_version["configuration"] + elif self.args.config: + # Load config from YAML file + self.config = yaml.safe_load(self.args.config) + logger.info( + f"Running with local configuration from {self.args.config.name}" + ) + else: + self.config = {} + logger.warning("Running without any extra configuration") + def add_arguments(self): """Override this method to add argparse argument to this worker""" @@ -208,6 +239,9 @@ class ElementsWorker(BaseWorker): assert all( isinstance(coord, (int, float)) for point in polygon for coord in point ), "polygon points should be lists of two numbers" + if self.is_read_only: + logger.warning("Cannot create element as this worker is in read-only mode") + return sub_element = self.api_client.request( "CreateElement", @@ -241,6 +275,11 @@ class ElementsWorker(BaseWorker): assert ( score and isinstance(score, float) and 0 <= score <= 1 ), "score shouldn't be null and should be a float in [0..1] range" + if self.is_read_only: + logger.warning( + "Cannot create transcription as this worker is in read-only mode" + ) + return self.api_client.request( "CreateTranscription", @@ -272,6 +311,11 @@ class ElementsWorker(BaseWorker): assert high_confidence and isinstance( high_confidence, bool ), "high_confidence shouldn't be null and should be of type bool" + if self.is_read_only: + logger.warning( + "Cannot create classification as this worker is in read-only mode" + ) + return self.api_client.request( "CreateClassification", @@ -306,6 +350,9 @@ class ElementsWorker(BaseWorker): assert isinstance(metas, dict), "metas should be of type dict" if validated: assert isinstance(validated, bool), "validated should be of type bool" + if self.is_read_only: + logger.warning("Cannot create entity as this worker is in read-only mode") + return entity = self.api_client.request( "CreateEntity", @@ -365,6 +412,11 @@ class ElementsWorker(BaseWorker): assert all( isinstance(coord, (int, float)) for point in polygon for coord in point ), f"Transcription at index {index} in transcriptions: polygon points should be lists of two numbers" + if self.is_read_only: + logger.warning( + "Cannot create transcriptions as this worker is in read-only mode" + ) + return annotations = self.api_client.request( "CreateElementTranscriptions", diff --git a/tests/conftest.py b/tests/conftest.py index d98065c802ff17cc3a81b71e8374abf6b09d72d6..e4f8a4d1ddcac251c17f387aeda6b082dc4fb567 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,15 +1,82 @@ # -*- coding: utf-8 -*- +import json import os +import sys +from pathlib import Path import pytest +from arkindex_worker.worker import ElementsWorker + @pytest.fixture(autouse=True) -def pass_schema(responses): +def setup_api(responses, monkeypatch): + + # Always use the environment variable first schema_url = os.environ.get("ARKINDEX_API_SCHEMA_URL") + if schema_url is None: + # Try to load a local schema as the current developer of base-worker + # may also work on the backend nearby + paths = [ + "~/dev/ark/backend/schema.yml", + "~/dev/ark/backend/output/schema.yml", + ] + for path in paths: + path = Path(path).expanduser().absolute() + print(path) + if path.exists(): + monkeypatch.setenv("ARKINDEX_API_SCHEMA_URL", str(path)) + schema_url = str(path) + break + + # Fallback to prod environment + if schema_url is None: + schema_url = "https://arkindex.teklia.com/api/v1/openapi/?format=openapi-json" + + # Allow accessing remote API schemas responses.add_passthru(schema_url) + # Force api requests on a dummy server with dummy credentials + monkeypatch.setenv("ARKINDEX_API_URL", "http://testserver/api/v1") + monkeypatch.setenv("ARKINDEX_API_TOKEN", "unittest1234") + @pytest.fixture(autouse=True) def give_worker_version_id_env_variable(monkeypatch): monkeypatch.setenv("WORKER_VERSION_ID", "12341234-1234-1234-1234-123412341234") + + +@pytest.fixture +def mock_worker_version_api(responses): + """Provide a mock API response to get worker configuration""" + payload = { + "id": "12341234-1234-1234-1234-123412341234", + "configuration": {"someKey": "someValue"}, + "revision": "deadbeef", + "docker_image": "python:3", + "docker_image_name": "python:3", + "state": "created", + "worker": { + "id": "deadbeef-1234-5678-1234-worker", + "name": "Fake worker", + "slug": "fake_worker", + "type": "classifier", + }, + } + responses.add( + responses.GET, + "http://testserver/api/v1/workers/versions/12341234-1234-1234-1234-123412341234/", + status=200, + body=json.dumps(payload), + content_type="application/json", + ) + + +@pytest.fixture +def mock_elements_worker(monkeypatch, mock_worker_version_api): + """Build and configure an ElementsWorker with fixed CLI parameters to avoid issues with pytest""" + monkeypatch.setattr(sys, "argv", ["worker"]) + + worker = ElementsWorker() + worker.configure() + return worker diff --git a/tests/test_base_worker.py b/tests/test_base_worker.py index fb9284ad5be8c4eaa82a1028d8e7c8e4a54dd70e..bb329321a9aaed5eab224c987bc301172edaed20 100644 --- a/tests/test_base_worker.py +++ b/tests/test_base_worker.py @@ -4,8 +4,6 @@ import os import sys from pathlib import Path -import pytest - from arkindex_worker import logger from arkindex_worker.worker import BaseWorker @@ -36,21 +34,39 @@ def test_init_var_ponos_data_given(monkeypatch): def test_init_var_worker_version_id_missing(monkeypatch): + monkeypatch.setattr(sys, "argv", ["worker"]) monkeypatch.delenv("WORKER_VERSION_ID") - with pytest.raises(Exception) as e: - BaseWorker() - assert ( - str(e.value) - == "Missing WORKER_VERSION_ID environment variable to start the Worker" - ) + worker = BaseWorker() + worker.configure() + assert worker.worker_version_id is None + assert worker.is_read_only is True + assert worker.config == {} # default empty case + +def test_init_var_worker_local_file(monkeypatch, tmp_path): + # Build a dummy yaml config file + config = tmp_path / "config.yml" + config.write_text("---\nlocalKey: abcdef123") -def test_cli_default(mocker): + monkeypatch.setattr(sys, "argv", ["worker", "-c", str(config)]) + monkeypatch.delenv("WORKER_VERSION_ID") + worker = BaseWorker() + worker.configure() + assert worker.worker_version_id is None + assert worker.is_read_only is True + assert worker.config == {"localKey": "abcdef123"} # Use a local file for devs + + config.unlink() + + +def test_cli_default(mocker, mock_worker_version_api): worker = BaseWorker() spy = mocker.spy(worker, "add_arguments") assert not spy.called assert logger.level == logging.NOTSET assert not hasattr(worker, "api_client") + + mocker.patch.object(sys, "argv", ["worker"]) worker.configure() assert spy.called @@ -58,11 +74,14 @@ def test_cli_default(mocker): assert not worker.args.verbose assert logger.level == logging.NOTSET assert worker.api_client + assert worker.worker_version_id == "12341234-1234-1234-1234-123412341234" + assert worker.is_read_only is False + assert worker.config == {"someKey": "someValue"} # from API logger.setLevel(logging.NOTSET) -def test_cli_arg_verbose_given(mocker): +def test_cli_arg_verbose_given(mocker, mock_worker_version_api): worker = BaseWorker() spy = mocker.spy(worker, "add_arguments") assert not spy.called @@ -77,5 +96,8 @@ def test_cli_arg_verbose_given(mocker): assert worker.args.verbose assert logger.level == logging.DEBUG assert worker.api_client + assert worker.worker_version_id == "12341234-1234-1234-1234-123412341234" + assert worker.is_read_only is False + assert worker.config == {"someKey": "someValue"} # from API logger.setLevel(logging.NOTSET) diff --git a/tests/test_elements_worker.py b/tests/test_elements_worker.py index e66df0f4d93719fbbe56ec16866b2b4d511868c2..3bc5f52c726d95793596608334a4c09fb9d1b711 100644 --- a/tests/test_elements_worker.py +++ b/tests/test_elements_worker.py @@ -31,7 +31,7 @@ TRANSCRIPTIONS_SAMPLE = [ ] -def test_cli_default(monkeypatch): +def test_cli_default(monkeypatch, mock_worker_version_api): _, path = tempfile.mkstemp() with open(path, "w") as f: json.dump( @@ -45,6 +45,7 @@ def test_cli_default(monkeypatch): ) monkeypatch.setenv("TASK_ELEMENTS", path) + monkeypatch.setattr(sys, "argv", ["worker"]) worker = ElementsWorker() worker.configure() @@ -53,7 +54,7 @@ def test_cli_default(monkeypatch): os.unlink(path) -def test_cli_arg_elements_list_given(mocker): +def test_cli_arg_elements_list_given(mocker, mock_worker_version_api): _, path = tempfile.mkstemp() with open(path, "w") as f: json.dump( @@ -75,14 +76,14 @@ def test_cli_arg_elements_list_given(mocker): os.unlink(path) -def test_cli_arg_element_one_given_not_uuid(mocker): +def test_cli_arg_element_one_given_not_uuid(mocker, mock_elements_worker): mocker.patch.object(sys, "argv", ["worker", "--element", "1234"]) worker = ElementsWorker() with pytest.raises(SystemExit): worker.configure() -def test_cli_arg_element_one_given(mocker): +def test_cli_arg_element_one_given(mocker, mock_elements_worker): mocker.patch.object( sys, "argv", ["worker", "--element", "12341234-1234-1234-1234-123412341234"] ) @@ -94,7 +95,7 @@ def test_cli_arg_element_one_given(mocker): assert not worker.args.elements_list -def test_cli_arg_element_many_given(mocker): +def test_cli_arg_element_many_given(mocker, mock_elements_worker): mocker.patch.object( sys, "argv", @@ -116,7 +117,7 @@ def test_cli_arg_element_many_given(mocker): assert not worker.args.elements_list -def test_list_elements_elements_list_arg_wrong_type(monkeypatch): +def test_list_elements_elements_list_arg_wrong_type(monkeypatch, mock_elements_worker): _, path = tempfile.mkstemp() with open(path, "w") as f: json.dump({}, f) @@ -131,7 +132,7 @@ def test_list_elements_elements_list_arg_wrong_type(monkeypatch): assert str(e.value) == "Elements list must be a list" -def test_list_elements_elements_list_arg_empty_list(monkeypatch): +def test_list_elements_elements_list_arg_empty_list(monkeypatch, mock_elements_worker): _, path = tempfile.mkstemp() with open(path, "w") as f: json.dump([], f) @@ -146,7 +147,7 @@ def test_list_elements_elements_list_arg_empty_list(monkeypatch): assert str(e.value) == "No elements in elements list" -def test_list_elements_elements_list_arg_missing_id(monkeypatch): +def test_list_elements_elements_list_arg_missing_id(monkeypatch, mock_elements_worker): _, path = tempfile.mkstemp() with open(path, "w") as f: json.dump([{"type": "volume"}], f) @@ -161,7 +162,7 @@ def test_list_elements_elements_list_arg_missing_id(monkeypatch): assert elt_list == [] -def test_list_elements_elements_list_arg(monkeypatch): +def test_list_elements_elements_list_arg(monkeypatch, mock_elements_worker): _, path = tempfile.mkstemp() with open(path, "w") as f: json.dump( @@ -184,7 +185,7 @@ def test_list_elements_elements_list_arg(monkeypatch): assert elt_list == ["volumeid", "pageid", "actid", "surfaceid"] -def test_list_elements_element_arg(mocker): +def test_list_elements_element_arg(mocker, mock_elements_worker): mocker.patch( "arkindex_worker.worker.argparse.ArgumentParser.parse_args", return_value=Namespace( @@ -200,7 +201,7 @@ def test_list_elements_element_arg(mocker): assert elt_list == ["volumeid", "pageid"] -def test_list_elements_both_args_error(mocker): +def test_list_elements_both_args_error(mocker, mock_elements_worker): _, path = tempfile.mkstemp() with open(path, "w") as f: json.dump( @@ -230,7 +231,7 @@ def test_list_elements_both_args_error(mocker): assert str(e.value) == "elements-list and element CLI args shouldn't be both set" -def test_create_sub_element_wrong_element(): +def test_create_sub_element_wrong_element(mock_elements_worker): worker = ElementsWorker() with pytest.raises(AssertionError) as e: worker.create_sub_element( @@ -251,7 +252,7 @@ def test_create_sub_element_wrong_element(): assert str(e.value) == "element shouldn't be null and should be of type Element" -def test_create_sub_element_wrong_type(): +def test_create_sub_element_wrong_type(mock_elements_worker): worker = ElementsWorker() elt = Element({"zone": None}) @@ -274,7 +275,7 @@ def test_create_sub_element_wrong_type(): assert str(e.value) == "type shouldn't be null and should be of type str" -def test_create_sub_element_wrong_name(): +def test_create_sub_element_wrong_name(mock_elements_worker): worker = ElementsWorker() elt = Element({"zone": None}) @@ -297,12 +298,11 @@ def test_create_sub_element_wrong_name(): assert str(e.value) == "name shouldn't be null and should be of type str" -def test_create_sub_element_wrong_polygon(): - worker = ElementsWorker() +def test_create_sub_element_wrong_polygon(mock_elements_worker): elt = Element({"zone": None}) with pytest.raises(AssertionError) as e: - worker.create_sub_element( + mock_elements_worker.create_sub_element( element=elt, type="something", name="0", @@ -311,7 +311,7 @@ def test_create_sub_element_wrong_polygon(): assert str(e.value) == "polygon shouldn't be null and should be of type list" with pytest.raises(AssertionError) as e: - worker.create_sub_element( + mock_elements_worker.create_sub_element( element=elt, type="something", name="O", @@ -320,7 +320,7 @@ def test_create_sub_element_wrong_polygon(): assert str(e.value) == "polygon shouldn't be null and should be of type list" with pytest.raises(AssertionError) as e: - worker.create_sub_element( + mock_elements_worker.create_sub_element( element=elt, type="something", name="O", @@ -329,7 +329,7 @@ def test_create_sub_element_wrong_polygon(): assert str(e.value) == "polygon should have at least three points" with pytest.raises(AssertionError) as e: - worker.create_sub_element( + mock_elements_worker.create_sub_element( element=elt, type="something", name="O", @@ -338,7 +338,7 @@ def test_create_sub_element_wrong_polygon(): assert str(e.value) == "polygon points should be lists of two items" with pytest.raises(AssertionError) as e: - worker.create_sub_element( + mock_elements_worker.create_sub_element( element=elt, type="something", name="O", @@ -347,7 +347,7 @@ def test_create_sub_element_wrong_polygon(): assert str(e.value) == "polygon points should be lists of two items" with pytest.raises(AssertionError) as e: - worker.create_sub_element( + mock_elements_worker.create_sub_element( element=elt, type="something", name="O", @@ -356,9 +356,7 @@ def test_create_sub_element_wrong_polygon(): assert str(e.value) == "polygon points should be lists of two numbers" -def test_create_sub_element_api_error(responses): - worker = ElementsWorker() - worker.configure() +def test_create_sub_element_api_error(responses, mock_elements_worker): elt = Element( { "id": "12341234-1234-1234-1234-123412341234", @@ -368,28 +366,26 @@ def test_create_sub_element_api_error(responses): ) responses.add( responses.POST, - "https://arkindex.teklia.com/api/v1/elements/create/", + "http://testserver/api/v1/elements/create/", status=500, ) with pytest.raises(ErrorResponse): - worker.create_sub_element( + mock_elements_worker.create_sub_element( element=elt, type="something", name="0", polygon=[[1, 1], [2, 2], [2, 1], [1, 2]], ) - assert len(responses.calls) == 1 - assert ( - responses.calls[0].request.url - == "https://arkindex.teklia.com/api/v1/elements/create/" - ) + assert len(responses.calls) == 2 + assert [call.request.url for call in responses.calls] == [ + "http://testserver/api/v1/workers/versions/12341234-1234-1234-1234-123412341234/", + "http://testserver/api/v1/elements/create/", + ] -def test_create_sub_element(responses): - worker = ElementsWorker() - worker.configure() +def test_create_sub_element(responses, mock_elements_worker): elt = Element( { "id": "12341234-1234-1234-1234-123412341234", @@ -399,24 +395,24 @@ def test_create_sub_element(responses): ) responses.add( responses.POST, - "https://arkindex.teklia.com/api/v1/elements/create/", + "http://testserver/api/v1/elements/create/", status=200, json={"id": "12345678-1234-1234-1234-123456789123"}, ) - sub_element_id = worker.create_sub_element( + sub_element_id = mock_elements_worker.create_sub_element( element=elt, type="something", name="0", polygon=[[1, 1], [2, 2], [2, 1], [1, 2]], ) - assert len(responses.calls) == 1 - assert ( - responses.calls[0].request.url - == "https://arkindex.teklia.com/api/v1/elements/create/" - ) - assert json.loads(responses.calls[0].request.body) == { + assert len(responses.calls) == 2 + assert [call.request.url for call in responses.calls] == [ + "http://testserver/api/v1/workers/versions/12341234-1234-1234-1234-123412341234/", + "http://testserver/api/v1/elements/create/", + ] + assert json.loads(responses.calls[1].request.body) == { "type": "something", "name": "0", "image": "22222222-2222-2222-2222-222222222222", @@ -428,10 +424,9 @@ def test_create_sub_element(responses): assert sub_element_id == "12345678-1234-1234-1234-123456789123" -def test_create_transcription_wrong_element(): - worker = ElementsWorker() +def test_create_transcription_wrong_element(mock_elements_worker): with pytest.raises(AssertionError) as e: - worker.create_transcription( + mock_elements_worker.create_transcription( element=None, text="i am a line", type=TranscriptionType.Line, @@ -440,7 +435,7 @@ def test_create_transcription_wrong_element(): assert str(e.value) == "element shouldn't be null and should be of type Element" with pytest.raises(AssertionError) as e: - worker.create_transcription( + mock_elements_worker.create_transcription( element="not element type", text="i am a line", type=TranscriptionType.Line, @@ -449,12 +444,11 @@ def test_create_transcription_wrong_element(): assert str(e.value) == "element shouldn't be null and should be of type Element" -def test_create_transcription_wrong_type(): - worker = ElementsWorker() +def test_create_transcription_wrong_type(mock_elements_worker): elt = Element({"id": "12341234-1234-1234-1234-123412341234"}) with pytest.raises(AssertionError) as e: - worker.create_transcription( + mock_elements_worker.create_transcription( element=elt, text="i am a line", type=None, @@ -465,7 +459,7 @@ def test_create_transcription_wrong_type(): ) with pytest.raises(AssertionError) as e: - worker.create_transcription( + mock_elements_worker.create_transcription( element=elt, text="i am a line", type=1234, @@ -476,7 +470,7 @@ def test_create_transcription_wrong_type(): ) with pytest.raises(AssertionError) as e: - worker.create_transcription( + mock_elements_worker.create_transcription( element=elt, text="i am a line", type="not_a_transcription_type", @@ -487,12 +481,11 @@ def test_create_transcription_wrong_type(): ) -def test_create_transcription_wrong_text(): - worker = ElementsWorker() +def test_create_transcription_wrong_text(mock_elements_worker): elt = Element({"id": "12341234-1234-1234-1234-123412341234"}) with pytest.raises(AssertionError) as e: - worker.create_transcription( + mock_elements_worker.create_transcription( element=elt, text=None, type=TranscriptionType.Line, @@ -501,7 +494,7 @@ def test_create_transcription_wrong_text(): assert str(e.value) == "text shouldn't be null and should be of type str" with pytest.raises(AssertionError) as e: - worker.create_transcription( + mock_elements_worker.create_transcription( element=elt, text=1234, type=TranscriptionType.Line, @@ -510,12 +503,11 @@ def test_create_transcription_wrong_text(): assert str(e.value) == "text shouldn't be null and should be of type str" -def test_create_transcription_wrong_score(): - worker = ElementsWorker() +def test_create_transcription_wrong_score(mock_elements_worker): elt = Element({"id": "12341234-1234-1234-1234-123412341234"}) with pytest.raises(AssertionError) as e: - worker.create_transcription( + mock_elements_worker.create_transcription( element=elt, text="i am a line", type=TranscriptionType.Line, @@ -526,7 +518,7 @@ def test_create_transcription_wrong_score(): ) with pytest.raises(AssertionError) as e: - worker.create_transcription( + mock_elements_worker.create_transcription( element=elt, text="i am a line", type=TranscriptionType.Line, @@ -537,7 +529,7 @@ def test_create_transcription_wrong_score(): ) with pytest.raises(AssertionError) as e: - worker.create_transcription( + mock_elements_worker.create_transcription( element=elt, text="i am a line", type=TranscriptionType.Line, @@ -548,7 +540,7 @@ def test_create_transcription_wrong_score(): ) with pytest.raises(AssertionError) as e: - worker.create_transcription( + mock_elements_worker.create_transcription( element=elt, text="i am a line", type=TranscriptionType.Line, @@ -559,54 +551,51 @@ def test_create_transcription_wrong_score(): ) -def test_create_transcription_api_error(responses): - worker = ElementsWorker() - worker.configure() +def test_create_transcription_api_error(responses, mock_elements_worker): elt = Element({"id": "12341234-1234-1234-1234-123412341234"}) responses.add( responses.POST, - f"https://arkindex.teklia.com/api/v1/element/{elt.id}/transcription/", + f"http://testserver/api/v1/element/{elt.id}/transcription/", status=500, ) with pytest.raises(ErrorResponse): - worker.create_transcription( + mock_elements_worker.create_transcription( element=elt, text="i am a line", type=TranscriptionType.Line, score=0.42, ) - assert len(responses.calls) == 1 - assert ( - responses.calls[0].request.url - == f"https://arkindex.teklia.com/api/v1/element/{elt.id}/transcription/" - ) + assert len(responses.calls) == 2 + assert [call.request.url for call in responses.calls] == [ + "http://testserver/api/v1/workers/versions/12341234-1234-1234-1234-123412341234/", + f"http://testserver/api/v1/element/{elt.id}/transcription/", + ] -def test_create_transcription(responses): - worker = ElementsWorker() - worker.configure() +def test_create_transcription(responses, mock_elements_worker): elt = Element({"id": "12341234-1234-1234-1234-123412341234"}) responses.add( responses.POST, - f"https://arkindex.teklia.com/api/v1/element/{elt.id}/transcription/", + f"http://testserver/api/v1/element/{elt.id}/transcription/", status=200, ) - worker.create_transcription( + mock_elements_worker.create_transcription( element=elt, text="i am a line", type=TranscriptionType.Line, score=0.42, ) - assert len(responses.calls) == 1 - assert ( - responses.calls[0].request.url - == f"https://arkindex.teklia.com/api/v1/element/{elt.id}/transcription/" - ) - assert json.loads(responses.calls[0].request.body) == { + assert len(responses.calls) == 2 + assert [call.request.url for call in responses.calls] == [ + "http://testserver/api/v1/workers/versions/12341234-1234-1234-1234-123412341234/", + f"http://testserver/api/v1/element/{elt.id}/transcription/", + ] + + assert json.loads(responses.calls[1].request.body) == { "text": "i am a line", "type": "line", "worker_version": "12341234-1234-1234-1234-123412341234", @@ -614,10 +603,9 @@ def test_create_transcription(responses): } -def test_create_classification_wrong_element(): - worker = ElementsWorker() +def test_create_classification_wrong_element(mock_elements_worker): with pytest.raises(AssertionError) as e: - worker.create_classification( + mock_elements_worker.create_classification( element=None, ml_class="a_class", confidence=0.42, @@ -626,7 +614,7 @@ def test_create_classification_wrong_element(): assert str(e.value) == "element shouldn't be null and should be of type Element" with pytest.raises(AssertionError) as e: - worker.create_classification( + mock_elements_worker.create_classification( element="not element type", ml_class="a_class", confidence=0.42, @@ -635,12 +623,11 @@ def test_create_classification_wrong_element(): assert str(e.value) == "element shouldn't be null and should be of type Element" -def test_create_classification_wrong_ml_class(): - worker = ElementsWorker() +def test_create_classification_wrong_ml_class(mock_elements_worker): elt = Element({"id": "12341234-1234-1234-1234-123412341234"}) with pytest.raises(AssertionError) as e: - worker.create_classification( + mock_elements_worker.create_classification( element=elt, ml_class=None, confidence=0.42, @@ -649,7 +636,7 @@ def test_create_classification_wrong_ml_class(): assert str(e.value) == "ml_class shouldn't be null and should be of type str" with pytest.raises(AssertionError) as e: - worker.create_classification( + mock_elements_worker.create_classification( element=elt, ml_class=1234, confidence=0.42, @@ -658,12 +645,11 @@ def test_create_classification_wrong_ml_class(): assert str(e.value) == "ml_class shouldn't be null and should be of type str" -def test_create_classification_wrong_confidence(): - worker = ElementsWorker() +def test_create_classification_wrong_confidence(mock_elements_worker): elt = Element({"id": "12341234-1234-1234-1234-123412341234"}) with pytest.raises(AssertionError) as e: - worker.create_classification( + mock_elements_worker.create_classification( element=elt, ml_class="a_class", confidence=None, @@ -675,7 +661,7 @@ def test_create_classification_wrong_confidence(): ) with pytest.raises(AssertionError) as e: - worker.create_classification( + mock_elements_worker.create_classification( element=elt, ml_class="a_class", confidence="wrong confidence", @@ -687,7 +673,7 @@ def test_create_classification_wrong_confidence(): ) with pytest.raises(AssertionError) as e: - worker.create_classification( + mock_elements_worker.create_classification( element=elt, ml_class="a_class", confidence=0, @@ -699,7 +685,7 @@ def test_create_classification_wrong_confidence(): ) with pytest.raises(AssertionError) as e: - worker.create_classification( + mock_elements_worker.create_classification( element=elt, ml_class="a_class", confidence=2.00, @@ -711,12 +697,11 @@ def test_create_classification_wrong_confidence(): ) -def test_create_classification_wrong_high_confidence(): - worker = ElementsWorker() +def test_create_classification_wrong_high_confidence(mock_elements_worker): elt = Element({"id": "12341234-1234-1234-1234-123412341234"}) with pytest.raises(AssertionError) as e: - worker.create_classification( + mock_elements_worker.create_classification( element=elt, ml_class="a_class", confidence=0.42, @@ -727,7 +712,7 @@ def test_create_classification_wrong_high_confidence(): ) with pytest.raises(AssertionError) as e: - worker.create_classification( + mock_elements_worker.create_classification( element=elt, ml_class="a_class", confidence=0.42, @@ -738,54 +723,51 @@ def test_create_classification_wrong_high_confidence(): ) -def test_create_classification_api_error(responses): - worker = ElementsWorker() - worker.configure() +def test_create_classification_api_error(responses, mock_elements_worker): elt = Element({"id": "12341234-1234-1234-1234-123412341234"}) responses.add( responses.POST, - "https://arkindex.teklia.com/api/v1/classifications/", + "http://testserver/api/v1/classifications/", status=500, ) with pytest.raises(ErrorResponse): - worker.create_classification( + mock_elements_worker.create_classification( element=elt, ml_class="a_class", confidence=0.42, high_confidence=True, ) - assert len(responses.calls) == 1 - assert ( - responses.calls[0].request.url - == "https://arkindex.teklia.com/api/v1/classifications/" - ) + assert len(responses.calls) == 2 + assert [call.request.url for call in responses.calls] == [ + "http://testserver/api/v1/workers/versions/12341234-1234-1234-1234-123412341234/", + "http://testserver/api/v1/classifications/", + ] -def test_create_classification(responses): - worker = ElementsWorker() - worker.configure() +def test_create_classification(responses, mock_elements_worker): elt = Element({"id": "12341234-1234-1234-1234-123412341234"}) responses.add( responses.POST, - "https://arkindex.teklia.com/api/v1/classifications/", + "http://testserver/api/v1/classifications/", status=200, ) - worker.create_classification( + mock_elements_worker.create_classification( element=elt, ml_class="a_class", confidence=0.42, high_confidence=True, ) - assert len(responses.calls) == 1 - assert ( - responses.calls[0].request.url - == "https://arkindex.teklia.com/api/v1/classifications/" - ) - assert json.loads(responses.calls[0].request.body) == { + assert len(responses.calls) == 2 + assert [call.request.url for call in responses.calls] == [ + "http://testserver/api/v1/workers/versions/12341234-1234-1234-1234-123412341234/", + "http://testserver/api/v1/classifications/", + ] + + assert json.loads(responses.calls[1].request.body) == { "element": "12341234-1234-1234-1234-123412341234", "ml_class": "a_class", "worker_version": "12341234-1234-1234-1234-123412341234", @@ -794,10 +776,9 @@ def test_create_classification(responses): } -def test_create_entity_wrong_element(): - worker = ElementsWorker() +def test_create_entity_wrong_element(mock_elements_worker): with pytest.raises(AssertionError) as e: - worker.create_entity( + mock_elements_worker.create_entity( element="not element type", name="Bob Bob", type=EntityType.Person, @@ -806,7 +787,7 @@ def test_create_entity_wrong_element(): assert str(e.value) == "element shouldn't be null and should be of type Element" with pytest.raises(AssertionError) as e: - worker.create_entity( + mock_elements_worker.create_entity( element="not element type", name="Bob Bob", type=EntityType.Person, @@ -815,12 +796,11 @@ def test_create_entity_wrong_element(): assert str(e.value) == "element shouldn't be null and should be of type Element" -def test_create_entity_wrong_name(): - worker = ElementsWorker() +def test_create_entity_wrong_name(mock_elements_worker): elt = Element({"id": "12341234-1234-1234-1234-123412341234"}) with pytest.raises(AssertionError) as e: - worker.create_entity( + mock_elements_worker.create_entity( element=elt, name=None, type=EntityType.Person, @@ -829,7 +809,7 @@ def test_create_entity_wrong_name(): assert str(e.value) == "name shouldn't be null and should be of type str" with pytest.raises(AssertionError) as e: - worker.create_entity( + mock_elements_worker.create_entity( element=elt, name=1234, type=EntityType.Person, @@ -838,12 +818,11 @@ def test_create_entity_wrong_name(): assert str(e.value) == "name shouldn't be null and should be of type str" -def test_create_entity_wrong_type(): - worker = ElementsWorker() +def test_create_entity_wrong_type(mock_elements_worker): elt = Element({"id": "12341234-1234-1234-1234-123412341234"}) with pytest.raises(AssertionError) as e: - worker.create_entity( + mock_elements_worker.create_entity( element=elt, name="Bob Bob", type=None, @@ -852,7 +831,7 @@ def test_create_entity_wrong_type(): assert str(e.value) == "type shouldn't be null and should be of type EntityType" with pytest.raises(AssertionError) as e: - worker.create_entity( + mock_elements_worker.create_entity( element=elt, name="Bob Bob", type=1234, @@ -861,7 +840,7 @@ def test_create_entity_wrong_type(): assert str(e.value) == "type shouldn't be null and should be of type EntityType" with pytest.raises(AssertionError) as e: - worker.create_entity( + mock_elements_worker.create_entity( element=elt, name="Bob Bob", type="not_an_entity_type", @@ -870,12 +849,11 @@ def test_create_entity_wrong_type(): assert str(e.value) == "type shouldn't be null and should be of type EntityType" -def test_create_entity_wrong_corpus(): - worker = ElementsWorker() +def test_create_entity_wrong_corpus(mock_elements_worker): elt = Element({"id": "12341234-1234-1234-1234-123412341234"}) with pytest.raises(AssertionError) as e: - worker.create_entity( + mock_elements_worker.create_entity( element=elt, name="Bob Bob", type=EntityType.Person, @@ -884,7 +862,7 @@ def test_create_entity_wrong_corpus(): assert str(e.value) == "corpus shouldn't be null and should be of type str" with pytest.raises(AssertionError) as e: - worker.create_entity( + mock_elements_worker.create_entity( element=elt, name="Bob Bob", type=EntityType.Person, @@ -893,12 +871,11 @@ def test_create_entity_wrong_corpus(): assert str(e.value) == "corpus shouldn't be null and should be of type str" -def test_create_entity_wrong_metas(): - worker = ElementsWorker() +def test_create_entity_wrong_metas(mock_elements_worker): elt = Element({"id": "12341234-1234-1234-1234-123412341234"}) with pytest.raises(AssertionError) as e: - worker.create_entity( + mock_elements_worker.create_entity( element=elt, name="Bob Bob", type=EntityType.Person, @@ -908,12 +885,11 @@ def test_create_entity_wrong_metas(): assert str(e.value) == "metas should be of type dict" -def test_create_entity_wrong_validated(): - worker = ElementsWorker() +def test_create_entity_wrong_validated(mock_elements_worker): elt = Element({"id": "12341234-1234-1234-1234-123412341234"}) with pytest.raises(AssertionError) as e: - worker.create_entity( + mock_elements_worker.create_entity( element=elt, name="Bob Bob", type=EntityType.Person, @@ -923,53 +899,51 @@ def test_create_entity_wrong_validated(): assert str(e.value) == "validated should be of type bool" -def test_create_entity_api_error(responses): - worker = ElementsWorker() - worker.configure() +def test_create_entity_api_error(responses, mock_elements_worker): elt = Element({"id": "12341234-1234-1234-1234-123412341234"}) responses.add( responses.POST, - "https://arkindex.teklia.com/api/v1/entity/", + "http://testserver/api/v1/entity/", status=500, ) with pytest.raises(ErrorResponse): - worker.create_entity( + mock_elements_worker.create_entity( element=elt, name="Bob Bob", type=EntityType.Person, corpus="12341234-1234-1234-1234-123412341234", ) - assert len(responses.calls) == 1 - assert ( - responses.calls[0].request.url == "https://arkindex.teklia.com/api/v1/entity/" - ) + assert len(responses.calls) == 2 + assert [call.request.url for call in responses.calls] == [ + "http://testserver/api/v1/workers/versions/12341234-1234-1234-1234-123412341234/", + "http://testserver/api/v1/entity/", + ] -def test_create_entity(responses): - worker = ElementsWorker() - worker.configure() +def test_create_entity(responses, mock_elements_worker): elt = Element({"id": "12341234-1234-1234-1234-123412341234"}) responses.add( responses.POST, - "https://arkindex.teklia.com/api/v1/entity/", + "http://testserver/api/v1/entity/", status=200, json={"id": "12345678-1234-1234-1234-123456789123"}, ) - entity_id = worker.create_entity( + entity_id = mock_elements_worker.create_entity( element=elt, name="Bob Bob", type=EntityType.Person, corpus="12341234-1234-1234-1234-123412341234", ) - assert len(responses.calls) == 1 - assert ( - responses.calls[0].request.url == "https://arkindex.teklia.com/api/v1/entity/" - ) - assert json.loads(responses.calls[0].request.body) == { + assert len(responses.calls) == 2 + assert [call.request.url for call in responses.calls] == [ + "http://testserver/api/v1/workers/versions/12341234-1234-1234-1234-123412341234/", + "http://testserver/api/v1/entity/", + ] + assert json.loads(responses.calls[1].request.body) == { "name": "Bob Bob", "type": "person", "metas": None, @@ -980,10 +954,9 @@ def test_create_entity(responses): assert entity_id == "12345678-1234-1234-1234-123456789123" -def test_create_element_transcriptions_wrong_element(): - worker = ElementsWorker() +def test_create_element_transcriptions_wrong_element(mock_elements_worker): with pytest.raises(AssertionError) as e: - worker.create_element_transcriptions( + mock_elements_worker.create_element_transcriptions( element=None, sub_element_type="page", transcription_type=TranscriptionType.Word, @@ -992,7 +965,7 @@ def test_create_element_transcriptions_wrong_element(): assert str(e.value) == "element shouldn't be null and should be of type Element" with pytest.raises(AssertionError) as e: - worker.create_element_transcriptions( + mock_elements_worker.create_element_transcriptions( element="not element type", sub_element_type="page", transcription_type=TranscriptionType.Word, @@ -1001,12 +974,11 @@ def test_create_element_transcriptions_wrong_element(): assert str(e.value) == "element shouldn't be null and should be of type Element" -def test_create_element_transcriptions_wrong_sub_element_type(): - worker = ElementsWorker() +def test_create_element_transcriptions_wrong_sub_element_type(mock_elements_worker): elt = Element({"zone": None}) with pytest.raises(AssertionError) as e: - worker.create_element_transcriptions( + mock_elements_worker.create_element_transcriptions( element=elt, sub_element_type=None, transcription_type=TranscriptionType.Word, @@ -1017,7 +989,7 @@ def test_create_element_transcriptions_wrong_sub_element_type(): ) with pytest.raises(AssertionError) as e: - worker.create_element_transcriptions( + mock_elements_worker.create_element_transcriptions( element=elt, sub_element_type=1234, transcription_type=TranscriptionType.Word, @@ -1028,12 +1000,11 @@ def test_create_element_transcriptions_wrong_sub_element_type(): ) -def test_create_element_transcriptions_wrong_transcription_type(): - worker = ElementsWorker() +def test_create_element_transcriptions_wrong_transcription_type(mock_elements_worker): elt = Element({"id": "12341234-1234-1234-1234-123412341234"}) with pytest.raises(AssertionError) as e: - worker.create_element_transcriptions( + mock_elements_worker.create_element_transcriptions( element=elt, sub_element_type="page", transcription_type=None, @@ -1045,7 +1016,7 @@ def test_create_element_transcriptions_wrong_transcription_type(): ) with pytest.raises(AssertionError) as e: - worker.create_element_transcriptions( + mock_elements_worker.create_element_transcriptions( element=elt, sub_element_type="page", transcription_type=1234, @@ -1057,7 +1028,7 @@ def test_create_element_transcriptions_wrong_transcription_type(): ) with pytest.raises(AssertionError) as e: - worker.create_element_transcriptions( + mock_elements_worker.create_element_transcriptions( element=elt, sub_element_type="page", transcription_type="not_a_transcription_type", @@ -1069,12 +1040,11 @@ def test_create_element_transcriptions_wrong_transcription_type(): ) -def test_create_element_transcriptions_wrong_transcriptions(): - worker = ElementsWorker() +def test_create_element_transcriptions_wrong_transcriptions(mock_elements_worker): elt = Element({"zone": None}) with pytest.raises(AssertionError) as e: - worker.create_element_transcriptions( + mock_elements_worker.create_element_transcriptions( element=elt, sub_element_type="page", transcription_type=TranscriptionType.Word, @@ -1083,7 +1053,7 @@ def test_create_element_transcriptions_wrong_transcriptions(): assert str(e.value) == "transcriptions shouldn't be null and should be of type list" with pytest.raises(AssertionError) as e: - worker.create_element_transcriptions( + mock_elements_worker.create_element_transcriptions( element=elt, sub_element_type="page", transcription_type=TranscriptionType.Word, @@ -1092,7 +1062,7 @@ def test_create_element_transcriptions_wrong_transcriptions(): assert str(e.value) == "transcriptions shouldn't be null and should be of type list" with pytest.raises(AssertionError) as e: - worker.create_element_transcriptions( + mock_elements_worker.create_element_transcriptions( element=elt, sub_element_type="page", transcription_type=TranscriptionType.Word, @@ -1114,7 +1084,7 @@ def test_create_element_transcriptions_wrong_transcriptions(): ) with pytest.raises(AssertionError) as e: - worker.create_element_transcriptions( + mock_elements_worker.create_element_transcriptions( element=elt, sub_element_type="page", transcription_type=TranscriptionType.Word, @@ -1137,7 +1107,7 @@ def test_create_element_transcriptions_wrong_transcriptions(): ) with pytest.raises(AssertionError) as e: - worker.create_element_transcriptions( + mock_elements_worker.create_element_transcriptions( element=elt, sub_element_type="page", transcription_type=TranscriptionType.Word, @@ -1160,7 +1130,7 @@ def test_create_element_transcriptions_wrong_transcriptions(): ) with pytest.raises(AssertionError) as e: - worker.create_element_transcriptions( + mock_elements_worker.create_element_transcriptions( element=elt, sub_element_type="page", transcription_type=TranscriptionType.Word, @@ -1182,7 +1152,7 @@ def test_create_element_transcriptions_wrong_transcriptions(): ) with pytest.raises(AssertionError) as e: - worker.create_element_transcriptions( + mock_elements_worker.create_element_transcriptions( element=elt, sub_element_type="page", transcription_type=TranscriptionType.Word, @@ -1205,7 +1175,7 @@ def test_create_element_transcriptions_wrong_transcriptions(): ) with pytest.raises(AssertionError) as e: - worker.create_element_transcriptions( + mock_elements_worker.create_element_transcriptions( element=elt, sub_element_type="page", transcription_type=TranscriptionType.Word, @@ -1228,7 +1198,7 @@ def test_create_element_transcriptions_wrong_transcriptions(): ) with pytest.raises(AssertionError) as e: - worker.create_element_transcriptions( + mock_elements_worker.create_element_transcriptions( element=elt, sub_element_type="page", transcription_type=TranscriptionType.Word, @@ -1251,7 +1221,7 @@ def test_create_element_transcriptions_wrong_transcriptions(): ) with pytest.raises(AssertionError) as e: - worker.create_element_transcriptions( + mock_elements_worker.create_element_transcriptions( element=elt, sub_element_type="page", transcription_type=TranscriptionType.Word, @@ -1274,7 +1244,7 @@ def test_create_element_transcriptions_wrong_transcriptions(): ) with pytest.raises(AssertionError) as e: - worker.create_element_transcriptions( + mock_elements_worker.create_element_transcriptions( element=elt, sub_element_type="page", transcription_type=TranscriptionType.Word, @@ -1293,7 +1263,7 @@ def test_create_element_transcriptions_wrong_transcriptions(): ) with pytest.raises(AssertionError) as e: - worker.create_element_transcriptions( + mock_elements_worker.create_element_transcriptions( element=elt, sub_element_type="page", transcription_type=TranscriptionType.Word, @@ -1312,7 +1282,7 @@ def test_create_element_transcriptions_wrong_transcriptions(): ) with pytest.raises(AssertionError) as e: - worker.create_element_transcriptions( + mock_elements_worker.create_element_transcriptions( element=elt, sub_element_type="page", transcription_type=TranscriptionType.Word, @@ -1331,7 +1301,7 @@ def test_create_element_transcriptions_wrong_transcriptions(): ) with pytest.raises(AssertionError) as e: - worker.create_element_transcriptions( + mock_elements_worker.create_element_transcriptions( element=elt, sub_element_type="page", transcription_type=TranscriptionType.Word, @@ -1350,7 +1320,7 @@ def test_create_element_transcriptions_wrong_transcriptions(): ) with pytest.raises(AssertionError) as e: - worker.create_element_transcriptions( + mock_elements_worker.create_element_transcriptions( element=elt, sub_element_type="page", transcription_type=TranscriptionType.Word, @@ -1373,7 +1343,7 @@ def test_create_element_transcriptions_wrong_transcriptions(): ) with pytest.raises(AssertionError) as e: - worker.create_element_transcriptions( + mock_elements_worker.create_element_transcriptions( element=elt, sub_element_type="page", transcription_type=TranscriptionType.Word, @@ -1392,7 +1362,7 @@ def test_create_element_transcriptions_wrong_transcriptions(): ) with pytest.raises(AssertionError) as e: - worker.create_element_transcriptions( + mock_elements_worker.create_element_transcriptions( element=elt, sub_element_type="page", transcription_type=TranscriptionType.Word, @@ -1415,38 +1385,34 @@ def test_create_element_transcriptions_wrong_transcriptions(): ) -def test_create_element_transcriptions_api_error(responses): - worker = ElementsWorker() - worker.configure() +def test_create_element_transcriptions_api_error(responses, mock_elements_worker): elt = Element({"id": "12341234-1234-1234-1234-123412341234"}) responses.add( responses.POST, - f"https://arkindex.teklia.com/api/v1/element/{elt.id}/transcriptions/bulk/", + f"http://testserver/api/v1/element/{elt.id}/transcriptions/bulk/", status=500, ) with pytest.raises(ErrorResponse): - worker.create_element_transcriptions( + mock_elements_worker.create_element_transcriptions( element=elt, sub_element_type="page", transcription_type=TranscriptionType.Word, transcriptions=TRANSCRIPTIONS_SAMPLE, ) - assert len(responses.calls) == 1 - assert ( - responses.calls[0].request.url - == f"https://arkindex.teklia.com/api/v1/element/{elt.id}/transcriptions/bulk/" - ) + assert len(responses.calls) == 2 + assert [call.request.url for call in responses.calls] == [ + "http://testserver/api/v1/workers/versions/12341234-1234-1234-1234-123412341234/", + f"http://testserver/api/v1/element/{elt.id}/transcriptions/bulk/", + ] -def test_create_element_transcriptions(responses): - worker = ElementsWorker() - worker.configure() +def test_create_element_transcriptions(responses, mock_elements_worker): elt = Element({"id": "12341234-1234-1234-1234-123412341234"}) responses.add( responses.POST, - f"https://arkindex.teklia.com/api/v1/element/{elt.id}/transcriptions/bulk/", + f"http://testserver/api/v1/element/{elt.id}/transcriptions/bulk/", status=200, json=[ {"id": "word1_1_1", "created": False}, @@ -1455,19 +1421,20 @@ def test_create_element_transcriptions(responses): ], ) - annotations = worker.create_element_transcriptions( + annotations = mock_elements_worker.create_element_transcriptions( element=elt, sub_element_type="page", transcription_type=TranscriptionType.Word, transcriptions=TRANSCRIPTIONS_SAMPLE, ) - assert len(responses.calls) == 1 - assert ( - responses.calls[0].request.url - == f"https://arkindex.teklia.com/api/v1/element/{elt.id}/transcriptions/bulk/" - ) - assert json.loads(responses.calls[0].request.body) == { + assert len(responses.calls) == 2 + assert [call.request.url for call in responses.calls] == [ + "http://testserver/api/v1/workers/versions/12341234-1234-1234-1234-123412341234/", + f"http://testserver/api/v1/element/{elt.id}/transcriptions/bulk/", + ] + + assert json.loads(responses.calls[1].request.body) == { "element_type": "page", "transcription_type": "word", "worker_version": "12341234-1234-1234-1234-123412341234", diff --git a/tox.ini b/tox.ini index a4fe98e9d3f93d0ee21c86154adf6f8c9d69c494..aa0cb057b524dbd1f80595c7e06f54953a378df5 100644 --- a/tox.ini +++ b/tox.ini @@ -5,7 +5,7 @@ skipsdist=True [testenv] passenv = ARKINDEX_API_SCHEMA_URL commands = - pytest + pytest {posargs} deps = -rrequirements.txt