diff --git a/dlite_entities_service/main.py b/dlite_entities_service/main.py index 8c05404..2df7ef1 100644 --- a/dlite_entities_service/main.py +++ b/dlite_entities_service/main.py @@ -103,7 +103,7 @@ async def get_entity( ], ) -> dict[str, Any]: """Get a SOFT entity.""" - uri = f"{CONFIG.base_url}/{version}/{name}" + uri = f"{str(CONFIG.base_url).rstrip('/')}/{version}/{name}" entity = get_backend().read(uri) if entity is None: raise HTTPException( diff --git a/tests/cli/test_login.py b/tests/cli/test_login.py index 08a4ddc..c123a49 100644 --- a/tests/cli/test_login.py +++ b/tests/cli/test_login.py @@ -24,13 +24,13 @@ def test_login( input_method: Literal["cli_option", "stdin"], httpx_mock: HTTPXMock, get_backend_user: GetBackendUserFixture, + live_backend: bool, ) -> None: """Test the `entities-service login` CLI command.""" - import re - from dlite_entities_service.cli._utils.global_settings import CONTEXT from dlite_entities_service.cli.main import APP from dlite_entities_service.models.auth import Token + from dlite_entities_service.service.config import CONFIG backend_user = get_backend_user() @@ -43,7 +43,7 @@ def test_login( # Mock the HTTPX response httpx_mock.add_response( - url=re.compile(r"^.*\/_auth\/token\/?$"), + url=f"{str(CONFIG.base_url).rstrip('/')}/_auth/token", method="POST", match_content=f"grant_type=password&username={username}&password={password}".encode(), json=mock_token.model_dump(), @@ -73,7 +73,10 @@ def test_login( assert "Username: " in result.stdout assert "Password: " in result.stdout - assert CONTEXT["token"] == mock_token, CONTEXT + assert CONTEXT["token"] is not None, CONTEXT + + if not live_backend: + assert CONTEXT["token"] == mock_token, CONTEXT def test_token_persistence( @@ -82,13 +85,13 @@ def test_token_persistence( static_dir: Path, random_valid_entity: dict[str, Any], get_backend_user: GetBackendUserFixture, + live_backend: bool, ) -> None: """Test that the token is persisted to the config file.""" - import re - from dlite_entities_service.cli._utils.global_settings import CONTEXT from dlite_entities_service.cli.main import APP from dlite_entities_service.models.auth import Token + from dlite_entities_service.service.config import CONFIG backend_user = get_backend_user(auth_role="readWrite") @@ -111,7 +114,7 @@ def test_token_persistence( # Mock the login HTTPX response httpx_mock.add_response( - url=re.compile(r"^.*\/_auth\/token\/?$"), + url=f"{str(CONFIG.base_url).rstrip('/')}/_auth/token", method="POST", match_content=f"grant_type=password&username={username}&password={password}".encode(), json=mock_token.model_dump(), @@ -123,7 +126,7 @@ def test_token_persistence( status_code=404, # not found, i.e., entity does not already exist ) httpx_mock.add_response( - url=re.compile(r"^.*\/_admin\/create_many\/?$"), + url=f"{str(CONFIG.base_url).rstrip('/')}/_admin/create_many", method="POST", match_headers={ "Authorization": f"{mock_token.token_type} {mock_token.access_token}" @@ -156,7 +159,10 @@ def test_token_persistence( stdout=result.stdout, stderr=result.stderr ) assert "Successfully logged in." in result.stdout.replace("\n", "") - assert CONTEXT["token"] == mock_token, CONTEXT + assert CONTEXT["token"] is not None, CONTEXT + + if not live_backend: + assert CONTEXT["token"] == mock_token, CONTEXT # Run the upload command again result = cli.invoke( @@ -171,17 +177,19 @@ def test_token_persistence( ) assert "Successfully uploaded 1 entity:" in result.stdout.replace("\n", "") assert not result.stderr - assert CONTEXT["token"] == mock_token, CONTEXT + assert CONTEXT["token"] is not None, CONTEXT + + if not live_backend: + assert CONTEXT["token"] == mock_token, CONTEXT def test_login_invalid_credentials( cli: CliRunner, httpx_mock: HTTPXMock, get_backend_user: GetBackendUserFixture ) -> None: """Test that the command fails with invalid credentials.""" - import re - from dlite_entities_service.cli._utils.global_settings import CONTEXT from dlite_entities_service.cli.main import APP + from dlite_entities_service.service.config import CONFIG username = "testuser" password = "testpassword" @@ -197,7 +205,7 @@ def test_login_invalid_credentials( # Mock the HTTPX response httpx_mock.add_response( - url=re.compile(r"^.*\/_auth\/token\/?$"), + url=f"{str(CONFIG.base_url).rstrip('/')}/_auth/token", method="POST", match_content=f"grant_type=password&username={username}&password={password}".encode(), status_code=401, # unauthorized @@ -223,12 +231,11 @@ def test_login_invalid_credentials( @pytest.mark.skip_if_live_backend("Does not raise HTTP errors in this case.") def test_http_errors(cli: CliRunner, httpx_mock: HTTPXMock) -> None: """Ensure proper error messages are given if an HTTP error occurs.""" - import re - from httpx import HTTPError from dlite_entities_service.cli._utils.global_settings import CONTEXT from dlite_entities_service.cli.main import APP + from dlite_entities_service.service.config import CONFIG username = "testuser" password = "testpassword" @@ -240,7 +247,7 @@ def test_http_errors(cli: CliRunner, httpx_mock: HTTPXMock) -> None: # Mock the login HTTPX response httpx_mock.add_exception( HTTPError(error_message), - url=re.compile(r"^.*\/_auth\/token\/?$"), + url=f"{str(CONFIG.base_url).rstrip('/')}/_auth/token", method="POST", match_content=f"grant_type=password&username={username}&password={password}".encode(), ) @@ -267,10 +274,9 @@ def test_json_decode_errors( cli: CliRunner, httpx_mock: HTTPXMock, return_status_code: Literal[200, 500] ) -> None: """Ensure proper error messages are given if a JSON decode error occurs.""" - import re - from dlite_entities_service.cli._utils.global_settings import CONTEXT from dlite_entities_service.cli.main import APP + from dlite_entities_service.service.config import CONFIG username = "testuser" password = "testpassword" @@ -279,7 +285,7 @@ def test_json_decode_errors( # Mock the login HTTPX response httpx_mock.add_response( - url=re.compile(r"^.*\/_auth\/token\/?$"), + url=f"{str(CONFIG.base_url).rstrip('/')}/_auth/token", method="POST", match_content=f"grant_type=password&username={username}&password={password}".encode(), status_code=return_status_code, @@ -305,10 +311,9 @@ def test_json_decode_errors( def test_validation_error(cli: CliRunner, httpx_mock: HTTPXMock) -> None: """Ensure proper error messages are given if the response cannot be parsed as a valid token.""" - import re - from dlite_entities_service.cli._utils.global_settings import CONTEXT from dlite_entities_service.cli.main import APP + from dlite_entities_service.service.config import CONFIG username = "testuser" password = "testpassword" @@ -317,7 +322,7 @@ def test_validation_error(cli: CliRunner, httpx_mock: HTTPXMock) -> None: # Mock the login HTTPX response httpx_mock.add_response( - url=re.compile(r"^.*\/_auth\/token\/?$"), + url=f"{str(CONFIG.base_url).rstrip('/')}/_auth/token", method="POST", match_content=f"grant_type=password&username={username}&password={password}".encode(), status_code=200, diff --git a/tests/cli/test_upload.py b/tests/cli/test_upload.py index 0d7b280..d0504de 100644 --- a/tests/cli/test_upload.py +++ b/tests/cli/test_upload.py @@ -38,9 +38,9 @@ def test_upload_filepath( ) -> None: """Test upload with a filepath.""" import json - import re from dlite_entities_service.cli import main + from dlite_entities_service.service.config import CONFIG entity_filepath = static_dir / "valid_entities" / "Person.json" raw_entity: dict[str, Any] = json.loads(entity_filepath.read_bytes()) @@ -54,7 +54,7 @@ def test_upload_filepath( # Mock response for "Upload entities" httpx_mock.add_response( - url=re.compile(r"^.*\/_admin\/create_many\/?$"), + url=f"{str(CONFIG.base_url).rstrip('/')}/_admin/create_many", method="POST", match_headers={"Authorization": "Bearer mock_token"}, match_json=[raw_entity], @@ -152,7 +152,7 @@ def test_upload_directory( # Mock response for "Upload entities" httpx_mock.add_response( - url=f"{CONFIG.base_url}/_admin/create_many", + url=f"{str(CONFIG.base_url).rstrip('/')}/_admin/create_many", method="POST", match_headers={"Authorization": "Bearer mock_token"}, status_code=201, # created @@ -377,9 +377,11 @@ def test_existing_entity_different_content( # Mock response for "Upload entities" new_entity_file_to_be_uploaded = deepcopy(new_entity) new_entity_file_to_be_uploaded["version"] = "0.1.1" - new_entity_file_to_be_uploaded["uri"] = f"{CONFIG.base_url}/0.1.1/Person" + new_entity_file_to_be_uploaded[ + "uri" + ] = f"{str(CONFIG.base_url).rstrip('/')}/0.1.1/Person" httpx_mock.add_response( - url=f"{CONFIG.base_url}/_admin/create_many", + url=f"{str(CONFIG.base_url).rstrip('/')}/_admin/create_many", method="POST", match_headers={"Authorization": "Bearer mock_token"}, match_json=[new_entity_file_to_be_uploaded], @@ -414,9 +416,11 @@ def test_existing_entity_different_content( # Mock response for "Upload entities" new_entity_file_to_be_uploaded = deepcopy(new_entity) new_entity_file_to_be_uploaded["version"] = custom_version - new_entity_file_to_be_uploaded["uri"] = f"{CONFIG.base_url}/{custom_version}/Person" + new_entity_file_to_be_uploaded[ + "uri" + ] = f"{str(CONFIG.base_url).rstrip('/')}/{custom_version}/Person" httpx_mock.add_response( - url=f"{CONFIG.base_url}/_admin/create_many", + url=f"{str(CONFIG.base_url).rstrip('/')}/_admin/create_many", method="POST", match_headers={"Authorization": "Bearer mock_token"}, match_json=[new_entity_file_to_be_uploaded], @@ -564,11 +568,10 @@ def test_http_errors( random_valid_entity: dict[str, Any], ) -> None: """Ensure proper error messages are given if an HTTP error occurs.""" - import re - from httpx import HTTPError from dlite_entities_service.cli.main import APP + from dlite_entities_service.service.config import CONFIG error_message = "Generic HTTP Error" @@ -608,7 +611,8 @@ def test_http_errors( status_code=404, # not found, i.e., entity does not already exist ) httpx_mock.add_exception( - HTTPError(error_message), url=re.compile(r"^.*_admin/create_many/?$") + HTTPError(error_message), + url=f"{str(CONFIG.base_url).rstrip('/')}/_admin/create_many", ) result = cli.invoke( @@ -636,9 +640,8 @@ def test_json_decode_errors( random_valid_entity: dict[str, Any], ) -> None: """Ensure proper error messages are given if a JSONDecodeError occurs.""" - import re - from dlite_entities_service.cli.main import APP + from dlite_entities_service.service.config import CONFIG if "uri" in random_valid_entity: entity_uri: str = random_valid_entity["uri"] @@ -676,7 +679,7 @@ def test_json_decode_errors( status_code=404, # not found, i.e., entity does not already exist ) httpx_mock.add_response( - url=re.compile(r"^.*_admin/create_many/?$"), + url=f"{str(CONFIG.base_url).rstrip('/')}/_admin/create_many", status_code=400, content=b"not json", ) @@ -707,9 +710,9 @@ def test_unable_to_upload( ) -> None: """Ensure a proper error message is given if an entity cannot be uploaded.""" import json - import re from dlite_entities_service.cli.main import APP + from dlite_entities_service.service.config import CONFIG if "uri" in random_valid_entity: entity_uri: str = random_valid_entity["uri"] @@ -729,7 +732,7 @@ def test_unable_to_upload( status_code=404, # not found, i.e., entity does not already exist ) httpx_mock.add_response( - url=re.compile(r"^.*\/_admin\/create_many\/?$"), + url=f"{str(CONFIG.base_url).rstrip('/')}/_admin/create_many", status_code=400, json=error_message, ) @@ -763,11 +766,10 @@ def test_global_option_token( httpx_mock: HTTPXMock, ) -> None: """Test that the token is correctly used when supplied using `--token`.""" - import re - from dlite_entities_service.cli._utils.global_settings import CONTEXT from dlite_entities_service.cli.main import APP from dlite_entities_service.models.auth import Token + from dlite_entities_service.service.config import CONFIG if "uri" in random_valid_entity: entity_uri: str = random_valid_entity["uri"] @@ -791,7 +793,7 @@ def test_global_option_token( # Mock response for "Upload entities" httpx_mock.add_response( - url=re.compile(r".*\/_admin\/create_many\/?$"), + url=f"{str(CONFIG.base_url).rstrip('/')}/_admin/create_many", method="POST", match_headers={ "Authorization": f"{mock_token.token_type} {mock_token.access_token}" diff --git a/tests/conftest.py b/tests/conftest.py index b2f79a4..df4adcb 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -463,6 +463,11 @@ def non_mocked_hosts(live_backend: bool) -> list[str]: import os if live_backend: - return [os.getenv("ENTITY_SERVICE_HOST", "localhost")] + host, port = os.getenv("ENTITY_SERVICE_HOST", "localhost"), os.getenv( + "ENTITY_SERVICE_PORT", "8000" + ) + + localhost = host + (f":{port}" if port else "") + return [localhost] return []