diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 6b7a05fd..9bd8468a 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -42,7 +42,7 @@ jobs: cache: "poetry" - run: poetry install - run: > - poetry run pytest + poetry run pytest -vv -m integration --cloud-api-key "${{ secrets.CLOUD_API_KEY }}" --enterprise-uri "https://test-api.lifecyclesolutions.ni.com" --enterprise-api-key "${{ secrets.ENTERPRISE_API_KEY }}" diff --git a/nisystemlink/clients/notebook/_notebook_client.py b/nisystemlink/clients/notebook/_notebook_client.py index b085f5e6..5032d4d4 100644 --- a/nisystemlink/clients/notebook/_notebook_client.py +++ b/nisystemlink/clients/notebook/_notebook_client.py @@ -15,7 +15,7 @@ ) from nisystemlink.clients.core.helpers._iterator_file_like import IteratorFileLike from requests.models import Response -from uplink import Part, Path, retry +from uplink import Part, Path, multipart, retry from . import models @@ -70,6 +70,7 @@ def get_notebook(self, id: str) -> models.NotebookMetadata: """ ... + @multipart @put("ninotebook/v1/notebook/{id}") def __update_notebook( self, @@ -115,8 +116,8 @@ def update_notebook( """ metadata_io = None if metadata is not None: - metadata_str = metadata.json() - metadata_io = io.BytesIO(metadata_str.encode("utf-8")) + metadata_str = metadata.json(by_alias=True, exclude_unset=True) + metadata_io = io.BytesIO(metadata_str.encode("ascii")) return self.__update_notebook( id=id, @@ -137,6 +138,7 @@ def delete_notebook(self, id: str) -> None: """ ... + @multipart @post("ninotebook/v1/notebook") def __create_notebook( self, @@ -176,9 +178,9 @@ def create_notebook( ApiException: if unable to communicate with the ``/ninotebook`` service or provided invalid arguments. """ - metadata_str = metadata.json() + metadata_str = metadata.json(by_alias=True, exclude_unset=True) - metadata_io = io.BytesIO(metadata_str.encode("utf-8")) + metadata_io = io.BytesIO(metadata_str.encode("ascii")) return self.__create_notebook( metadata=metadata_io, content=content, diff --git a/pyproject.toml b/pyproject.toml index cc8bab40..6b3c7353 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -73,6 +73,8 @@ markers = [ "slow: mark a test as a slow test", "focus: focus a specific test during development", ] +log_cli = "True" +log_cli_level = "INFO" [tool.black] exclude = ".*\\.pyi" diff --git a/tests/integration/notebook/test_notebook_client.py b/tests/integration/notebook/test_notebook_client.py index f259cc5f..2d334072 100644 --- a/tests/integration/notebook/test_notebook_client.py +++ b/tests/integration/notebook/test_notebook_client.py @@ -1,5 +1,7 @@ +import logging import string from random import choices +import typing import pytest import responses @@ -31,7 +33,9 @@ def random_filename() -> str: """Generate a random filename for each test in test class.""" rand_file_name = "".join(choices(string.ascii_letters + string.digits, k=10)) - return f"{PREFIX}{rand_file_name}.ipynb" + name = f"{PREFIX}{rand_file_name}.ipynb" + logging.info(f"Random filename: {name}") + return name @pytest.fixture() @@ -45,7 +49,11 @@ def _create_notebook( # file_bytes = file.read() # Read file as bytes with open("tests/integration/notebook/sample_file.ipynb", "rb") as file: - notebook_response = client.create_notebook(metadata=metadata, content=file) + try: + notebook_response = client.create_notebook(metadata=metadata, content=file) + except ApiException as e: + logging.warning(f"Error creating notebook: {metadata.json(by_alias=True, exclude_unset=True)}") + raise if cleanup: notebook_ids.append(notebook_response.id) @@ -58,6 +66,27 @@ def _create_notebook( client.delete_notebook(id=notebook_id) +@pytest.fixture() +def update_notebook(client: NotebookClient): + """Fixture to return a factory that updates a notebook.""" + def _update_notebook( + id: str, + metadata: NotebookMetadata = None, + content: typing.BinaryIO = None, + ) -> NotebookMetadata: + + try: + notebook_response = client.update_notebook(id=id, metadata=metadata, content=content) + except ApiException as e: + if (metadata is not None): + logging.warning(f"Error updating notebook {id}: {metadata.json(by_alias=True, exclude_unset=True)}") + raise + + return notebook_response + + return _update_notebook + + @pytest.mark.enterprise @pytest.mark.integration class TestNotebookClient: @@ -117,7 +146,7 @@ def test__get_notebook_with_invalid_id__raises_ApiException_NotFound(self, clien client.get_notebook(id="invalid_id") def test__update_existing_notebook_metadata__update_notebook_metadata_succeeds( - self, client: NotebookClient, create_notebook, random_filename + self, client: NotebookClient, create_notebook, update_notebook, random_filename ): metadata = NotebookMetadata(name=random_filename) notebook = create_notebook(metadata=metadata) @@ -127,7 +156,7 @@ def test__update_existing_notebook_metadata__update_notebook_metadata_succeeds( notebook.name = new_name notebook.properties = {"key": "value"} - response = client.update_notebook(id=notebook.id, metadata=notebook) + response = update_notebook(id=notebook.id, metadata=notebook) assert response.id == notebook.id assert response.name != random_filename @@ -135,13 +164,13 @@ def test__update_existing_notebook_metadata__update_notebook_metadata_succeeds( assert response.properties == notebook.properties def test__update_existing_notebook_content__update_notebook_content_succeeds( - self, client: NotebookClient, create_notebook, random_filename + self, client: NotebookClient, create_notebook, update_notebook, random_filename ): metadata = NotebookMetadata(name=random_filename) notebook = create_notebook(metadata=metadata) with open("tests/integration/notebook/sample_file.ipynb", "rb") as file: - response = client.update_notebook(id=notebook.id, content=file) + response = update_notebook(id=notebook.id, content=file) assert response.id == notebook.id @@ -153,17 +182,17 @@ def test__update_notebook_metadata_with_invalid_id__raises_ApiException_NotFound client.update_notebook(id="invalid_id", metadata=metadata) def test__update_notebook_content_with_invalid_file__raises_ApiException_BadRequest( - self, client: NotebookClient, create_notebook, random_filename + self, client: NotebookClient, create_notebook, update_notebook, random_filename ): metadata = NotebookMetadata(name=random_filename) notebook = create_notebook(metadata=metadata) with open("tests/integration/notebook/test_notebook_client.py", "rb") as file: with pytest.raises(ApiException, match="Bad Request"): - client.update_notebook(id=notebook.id, content=file) + update_notebook(id=notebook.id, content=file) def test__update_notebook_content_with_duplicate_file_name__raises_ApiException_BadRequest( - self, client: NotebookClient, create_notebook, random_filename + self, client: NotebookClient, create_notebook, update_notebook, random_filename ): metadata = NotebookMetadata(name=random_filename) notebook = create_notebook(metadata=metadata) @@ -172,17 +201,17 @@ def test__update_notebook_content_with_duplicate_file_name__raises_ApiException_ create_notebook(metadata=metadata) with pytest.raises(ApiException, match="409 Conflict"): - client.update_notebook(id=notebook.id, metadata=notebook) + update_notebook(id=notebook.id, metadata=notebook) def test__update_notebook_with_invalid_workspace__raises_ApiException_BadRequest( - self, client: NotebookClient, create_notebook, random_filename + self, client: NotebookClient, create_notebook, update_notebook, random_filename ): metadata = NotebookMetadata(name=random_filename) notebook = create_notebook(metadata=metadata) metadata.workspace = "invalid_workspace" with pytest.raises(ApiException, match="Bad Request"): - client.update_notebook(id=notebook.id, metadata=metadata) + update_notebook(id=notebook.id, metadata=metadata) def test__delete_notebook_with_valid_id__notebook_should_delete_successfully( self, client: NotebookClient, create_notebook, random_filename