From 6a1f35308af99fb1f27051fc66b2e0b373f88479 Mon Sep 17 00:00:00 2001 From: Richard Bell Date: Thu, 20 Mar 2025 17:05:20 -0500 Subject: [PATCH 1/8] Add logging --- .github/workflows/python-package.yml | 2 +- pyproject.toml | 1 + tests/integration/notebook/test_notebook_client.py | 5 ++++- 3 files changed, 6 insertions(+), 2 deletions(-) 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/pyproject.toml b/pyproject.toml index cc8bab40..04e7736a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -73,6 +73,7 @@ markers = [ "slow: mark a test as a slow test", "focus: focus a specific test during development", ] +log_cli = "True" [tool.black] exclude = ".*\\.pyi" diff --git a/tests/integration/notebook/test_notebook_client.py b/tests/integration/notebook/test_notebook_client.py index f259cc5f..a2fd0934 100644 --- a/tests/integration/notebook/test_notebook_client.py +++ b/tests/integration/notebook/test_notebook_client.py @@ -1,5 +1,6 @@ import string from random import choices +import logging import pytest import responses @@ -31,7 +32,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() From d90c3d26f8b90e6b8a43b3b178d0f5c22e680ec3 Mon Sep 17 00:00:00 2001 From: Richard Bell Date: Thu, 20 Mar 2025 17:17:23 -0500 Subject: [PATCH 2/8] Fix import order --- tests/integration/notebook/test_notebook_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/notebook/test_notebook_client.py b/tests/integration/notebook/test_notebook_client.py index a2fd0934..b64e0f31 100644 --- a/tests/integration/notebook/test_notebook_client.py +++ b/tests/integration/notebook/test_notebook_client.py @@ -1,6 +1,6 @@ import string -from random import choices import logging +from random import choices import pytest import responses From 498ba35842da417f76baafb52d1891d4dcc0b81e Mon Sep 17 00:00:00 2001 From: Richard Bell Date: Thu, 20 Mar 2025 17:23:44 -0500 Subject: [PATCH 3/8] Log info level --- pyproject.toml | 1 + tests/integration/notebook/test_notebook_client.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 04e7736a..6b3c7353 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -74,6 +74,7 @@ markers = [ "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 b64e0f31..f32b7531 100644 --- a/tests/integration/notebook/test_notebook_client.py +++ b/tests/integration/notebook/test_notebook_client.py @@ -1,5 +1,5 @@ -import string import logging +import string from random import choices import pytest From 7c60a612fb3a4d7cb415e4676160d3b747a120e6 Mon Sep 17 00:00:00 2001 From: Richard Bell Date: Thu, 20 Mar 2025 17:44:16 -0500 Subject: [PATCH 4/8] Add exception logging around create and update --- .../notebook/test_notebook_client.py | 47 ++++++++++++++----- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/tests/integration/notebook/test_notebook_client.py b/tests/integration/notebook/test_notebook_client.py index f32b7531..62733449 100644 --- a/tests/integration/notebook/test_notebook_client.py +++ b/tests/integration/notebook/test_notebook_client.py @@ -1,6 +1,7 @@ import logging import string from random import choices +import typing import pytest import responses @@ -48,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}") + raise if cleanup: notebook_ids.append(notebook_response.id) @@ -61,6 +66,26 @@ 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: + logging.warning(f"Error updating notebook {id}: {metadata}") + raise + + return notebook_response + + return _update_notebook + + @pytest.mark.enterprise @pytest.mark.integration class TestNotebookClient: @@ -120,7 +145,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) @@ -130,7 +155,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 @@ -138,13 +163,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 @@ -156,17 +181,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) @@ -175,17 +200,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 From 01390cf2b990f09da55bc7045faac827b223290b Mon Sep 17 00:00:00 2001 From: Richard Bell Date: Thu, 20 Mar 2025 18:04:53 -0500 Subject: [PATCH 5/8] Log as JSON --- tests/integration/notebook/test_notebook_client.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/integration/notebook/test_notebook_client.py b/tests/integration/notebook/test_notebook_client.py index 62733449..2d334072 100644 --- a/tests/integration/notebook/test_notebook_client.py +++ b/tests/integration/notebook/test_notebook_client.py @@ -52,7 +52,7 @@ def _create_notebook( try: notebook_response = client.create_notebook(metadata=metadata, content=file) except ApiException as e: - logging.warning(f"Error creating notebook: {metadata}") + logging.warning(f"Error creating notebook: {metadata.json(by_alias=True, exclude_unset=True)}") raise if cleanup: notebook_ids.append(notebook_response.id) @@ -78,7 +78,8 @@ def _update_notebook( try: notebook_response = client.update_notebook(id=id, metadata=metadata, content=content) except ApiException as e: - logging.warning(f"Error updating notebook {id}: {metadata}") + if (metadata is not None): + logging.warning(f"Error updating notebook {id}: {metadata.json(by_alias=True, exclude_unset=True)}") raise return notebook_response From f7448eb23cf4793a4d55f36492fb1f793076c786 Mon Sep 17 00:00:00 2001 From: Richard Bell Date: Thu, 20 Mar 2025 18:15:15 -0500 Subject: [PATCH 6/8] Match base client to json --- nisystemlink/clients/notebook/_notebook_client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nisystemlink/clients/notebook/_notebook_client.py b/nisystemlink/clients/notebook/_notebook_client.py index b085f5e6..70f43577 100644 --- a/nisystemlink/clients/notebook/_notebook_client.py +++ b/nisystemlink/clients/notebook/_notebook_client.py @@ -115,7 +115,7 @@ def update_notebook( """ metadata_io = None if metadata is not None: - metadata_str = metadata.json() + metadata_str = metadata.json(by_alias=True, exclude_unset=True) metadata_io = io.BytesIO(metadata_str.encode("utf-8")) return self.__update_notebook( @@ -176,7 +176,7 @@ 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")) return self.__create_notebook( From 1e4c9ccd387bb4c8b4aa0468f34f4e4711c20a87 Mon Sep 17 00:00:00 2001 From: Richard Bell Date: Thu, 20 Mar 2025 18:23:54 -0500 Subject: [PATCH 7/8] Add multi-part --- nisystemlink/clients/notebook/_notebook_client.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/nisystemlink/clients/notebook/_notebook_client.py b/nisystemlink/clients/notebook/_notebook_client.py index 70f43577..d2740e3b 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, @@ -137,6 +138,7 @@ def delete_notebook(self, id: str) -> None: """ ... + @multipart @post("ninotebook/v1/notebook") def __create_notebook( self, From dc42d1ad38a9fde1c3e3304b162626d8b952b1fc Mon Sep 17 00:00:00 2001 From: Richard Bell Date: Thu, 20 Mar 2025 18:32:57 -0500 Subject: [PATCH 8/8] Change to ascii encoding --- nisystemlink/clients/notebook/_notebook_client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nisystemlink/clients/notebook/_notebook_client.py b/nisystemlink/clients/notebook/_notebook_client.py index d2740e3b..5032d4d4 100644 --- a/nisystemlink/clients/notebook/_notebook_client.py +++ b/nisystemlink/clients/notebook/_notebook_client.py @@ -117,7 +117,7 @@ def update_notebook( metadata_io = None if metadata is not None: 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.__update_notebook( id=id, @@ -180,7 +180,7 @@ def create_notebook( """ 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,