From 573b07265feaf8668e966f9ba8408a6d18d50863 Mon Sep 17 00:00:00 2001 From: nh916 Date: Mon, 31 Jul 2023 13:56:30 -0700 Subject: [PATCH] Tests integration with `ON/OFF` switch (#237) * created `HAS_INTEGRATION_TESTS_ENABLED` boolean in conftest.py * test_api.py skipping tests if `HAS_INTEGRATION_TESTS_ENABLED` is turned OFF * turning `ON/OFF` integration API test with a simple boolean * skipping some tests in file and uncommenting them * updated `test_user.py` to remove unneeded fixture updated test to use standard fixture * added boolean variable for integration test ON/OFF switch `HAS_INTEGRATION_TESTS_ENABLED` * formatted with trunk * switched `HAS_INTEGRATION_TESTS_ENABLED` to `False` --- tests/api/test_api.py | 13 +-- tests/conftest.py | 2 + tests/nodes/supporting_nodes/test_file.py | 96 +++++++++++------------ tests/nodes/supporting_nodes/test_user.py | 37 ++------- tests/test_integration.py | 8 +- 5 files changed, 66 insertions(+), 90 deletions(-) diff --git a/tests/api/test_api.py b/tests/api/test_api.py index dee71126e..f25b7be86 100644 --- a/tests/api/test_api.py +++ b/tests/api/test_api.py @@ -8,6 +8,7 @@ import pytest import requests +from conftest import HAS_INTEGRATION_TESTS_ENABLED import cript from cript.api.exceptions import InvalidVocabulary @@ -41,7 +42,7 @@ def test_api_with_invalid_host() -> None: cript.API(host="no_http_host.org", api_token="123456789", storage_token="987654321") -@pytest.mark.skip(reason="skipping for now because it needs an API container") +@pytest.mark.skipif(not HAS_INTEGRATION_TESTS_ENABLED, reason="skipping because API client needs API token") def test_api_context(cript_api: cript.API) -> None: assert cript.api.api._global_cached_api is not None assert cript.api.api._get_global_cached_api() is not None @@ -252,7 +253,7 @@ def test_download_file_from_url(cript_api: cript.API, tmp_path) -> None: assert response == saved_file_contents -@pytest.mark.skip(reason="this test requires a real storage_token from a real frontend, and this cannot be done via CI") +@pytest.mark.skipif(not HAS_INTEGRATION_TESTS_ENABLED, reason="requires a real storage_token from a real frontend") def test_upload_and_download_local_file(cript_api, tmp_path_factory) -> None: """ tests file upload to cloud storage @@ -292,7 +293,7 @@ def test_upload_and_download_local_file(cript_api, tmp_path_factory) -> None: assert downloaded_file_contents == file_text -@pytest.mark.skip(reason="requires a real cript_api_token and not currently available on CI") +@pytest.mark.skipif(not HAS_INTEGRATION_TESTS_ENABLED, reason="requires a real cript_api_token") def test_api_search_node_type(cript_api: cript.API) -> None: """ tests the api.search() method with just a node type material search @@ -323,7 +324,7 @@ def test_api_search_node_type(cript_api: cript.API) -> None: assert materials_paginator.current_page_results[0]["name"] == "(2-Chlorophenyl) 2,4-dichlorobenzoate" -@pytest.mark.skip(reason="requires a real cript_api_token and not currently available on CI") +@pytest.mark.skipif(not HAS_INTEGRATION_TESTS_ENABLED, reason="requires a real cript_api_token") def test_api_search_contains_name(cript_api: cript.API) -> None: """ tests that it can correctly search with contains name mode @@ -336,7 +337,7 @@ def test_api_search_contains_name(cript_api: cript.API) -> None: assert contains_name_paginator.current_page_results[0]["name"] == "Pilocarpine polyacrylate" -@pytest.mark.skip(reason="requires a real cript_api_token and not currently available on CI") +@pytest.mark.skipif(not HAS_INTEGRATION_TESTS_ENABLED, reason="requires a real cript_api_token") def test_api_search_exact_name(cript_api: cript.API) -> None: """ tests search method with exact name search @@ -349,7 +350,7 @@ def test_api_search_exact_name(cript_api: cript.API) -> None: assert exact_name_paginator.current_page_results[0]["name"] == "Sodium polystyrene sulfonate" -@pytest.mark.skip(reason="requires a real cript_api_token and not currently available on CI") +@pytest.mark.skipif(not HAS_INTEGRATION_TESTS_ENABLED, reason="requires a real cript_api_token") def test_api_search_uuid(cript_api: cript.API) -> None: """ tests search with UUID diff --git a/tests/conftest.py b/tests/conftest.py index 1c14aba3f..817688ef9 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -16,6 +16,8 @@ import cript +HAS_INTEGRATION_TESTS_ENABLED: bool = False + @pytest.fixture(scope="session", autouse=True) def cript_api(): diff --git a/tests/nodes/supporting_nodes/test_file.py b/tests/nodes/supporting_nodes/test_file.py index 158a14e56..3a36db2cd 100644 --- a/tests/nodes/supporting_nodes/test_file.py +++ b/tests/nodes/supporting_nodes/test_file.py @@ -3,6 +3,7 @@ import os import uuid +import pytest from test_integration import integrate_nodes_helper from util import strip_uid_from_dict @@ -55,6 +56,7 @@ def test_source_is_local(tmp_path, tmp_path_factory) -> None: assert _is_local_file(file_source=relative_file_path) is True +@pytest.mark.skip(reason="test is outdated because files now upload on api.save()") def test_local_file_source_upload_and_download(tmp_path_factory) -> None: """ upload a file and download it and be sure the contents are the same @@ -68,70 +70,64 @@ def test_local_file_source_upload_and_download(tmp_path_factory) -> None: 1. download the file to a temporary path 1. read that file text and assert that the string written and read are the same """ - # import uuid - # import datetime - # file_text: str = ( - # f"This is an automated test from the Python SDK within " - # f"`tests/nodes/supporting_nodes/test_file.py/test_local_file_source_upload_and_download()` " - # f"checking that the file source is automatically and correctly uploaded to AWS S3. " - # f"The test is conducted on UTC time of '{datetime.datetime.utcnow()}' " - # f"with the unique UUID of '{str(uuid.uuid4())}'" - # ) - # - # # create a temp file and write to it - # upload_file_dir = tmp_path_factory.mktemp("file_test_upload_file_dir") - # local_file_path = upload_file_dir / "my_upload_file.txt" - # local_file_path.write_text(file_text) - # - # # create a file node with a local file path - # my_file = cript.File(name="my local file source node", source=str(local_file_path), type="data") - # - # # check that the file source has been uploaded to cloud storage and source has changed to reflect that - # assert my_file.source.startswith("tests/") - # - # # Get the temporary directory path and clean up handled by pytest - # download_file_dir = tmp_path_factory.mktemp("file_test_download_file_dir") - # download_file_name = "my_downloaded_file.txt" - # - # # download file - # my_file.download(destination_directory_path=download_file_dir, file_name=download_file_name) - # - # # the path the file was downloaded to and can be read from - # downloaded_local_file_path = download_file_dir / download_file_name - # - # # read file contents from where the file was downloaded - # downloaded_file_contents = downloaded_local_file_path.read_text() - # - # # assert file contents for upload and download are the same - # assert downloaded_file_contents == file_text - pass + import datetime + import uuid + + file_text: str = ( + f"This is an automated test from the Python SDK within " + f"`tests/nodes/supporting_nodes/test_file.py/test_local_file_source_upload_and_download()` " + f"checking that the file source is automatically and correctly uploaded to AWS S3. " + f"The test is conducted on UTC time of '{datetime.datetime.utcnow()}' " + f"with the unique UUID of '{str(uuid.uuid4())}'" + ) + + # create a temp file and write to it + upload_file_dir = tmp_path_factory.mktemp("file_test_upload_file_dir") + local_file_path = upload_file_dir / "my_upload_file.txt" + local_file_path.write_text(file_text) + + # create a file node with a local file path + my_file = cript.File(name="my local file source node", source=str(local_file_path), type="data") + + # check that the file source has been uploaded to cloud storage and source has changed to reflect that + assert my_file.source.startswith("tests/") + + # Get the temporary directory path and clean up handled by pytest + download_file_dir = tmp_path_factory.mktemp("file_test_download_file_dir") + download_file_name = "my_downloaded_file.txt" + + # download file + my_file.download(destination_directory_path=download_file_dir, file_name=download_file_name) + # the path the file was downloaded to and can be read from + downloaded_local_file_path = download_file_dir / download_file_name -def test_create_file_local_source(tmp_path) -> None: + # read file contents from where the file was downloaded + downloaded_file_contents = downloaded_local_file_path.read_text() + + # assert file contents for upload and download are the same + assert downloaded_file_contents == file_text + + +def test_create_file_with_local_source(tmp_path) -> None: """ tests that a simple file with only required attributes can be created with source pointing to a local file on storage create a temporary directory with temporary file """ - - # TODO since no S3 client token for GitHub CI this test will always fail. Commenting it out so tests run well # create a temporary file in the temporary directory to test with - # file_path = tmp_path / "test.txt" - # with open(file_path, "w") as temporary_file: - # temporary_file.write("hello world!") - # - # assert cript.File(name="my file node with local source", source=str(file_path), type="calibration") - pass + file_path = tmp_path / "test.txt" + with open(file_path, "w") as temporary_file: + temporary_file.write("hello world!") + + assert cript.File(name="my file node with local source", source=str(file_path), type="calibration") +@pytest.mark.skip(reason="validating file type automatically with DB schema and test not currently needed") def test_file_type_invalid_vocabulary() -> None: """ tests that setting the file type to an invalid vocabulary word gives the expected error - - Returns - ------- - None """ pass diff --git a/tests/nodes/supporting_nodes/test_user.py b/tests/nodes/supporting_nodes/test_user.py index 89fc9d6fc..ef2ee7ef9 100644 --- a/tests/nodes/supporting_nodes/test_user.py +++ b/tests/nodes/supporting_nodes/test_user.py @@ -36,47 +36,20 @@ def test_user_serialization_and_deserialization(complex_user_dict, complex_user_ assert strip_uid_from_dict(json.loads(user_node.json)) == user_node_dict -@pytest.fixture(scope="session") -def user_node() -> cript.User: - """ - create a user node for other tests to use - - Notes - ----- - User node should only be created from JSON and not from instantiation - - Returns - ------- - User - """ - # TODO create this user node from JSON instead of instantiation - # create User node - my_user = cript.User( - username="my username", - email="my_email@email.com", - orcid="123456", - ) - # use user node in test - yield my_user - - # reset user node - my_user = my_user - - -def test_set_user_properties(user_node): +def test_set_user_properties(complex_user_node): """ tests that setting any user property throws an AttributeError """ with pytest.raises(AttributeError): - user_node.username = "my new username" + complex_user_node.username = "my new username" with pytest.raises(AttributeError): - user_node.email = "my new email" + complex_user_node.email = "my new email" with pytest.raises(AttributeError): - user_node.orcid = "my new orcid" + complex_user_node.orcid = "my new orcid" with pytest.raises(AttributeError): # TODO try setting it via a group node # either way it should give the same error - user_node.orcid = ["my new group"] + complex_user_node.orcid = ["my new group"] diff --git a/tests/test_integration.py b/tests/test_integration.py index e916fd729..4cb27bbb2 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -1,6 +1,8 @@ import json import warnings +import pytest +from conftest import HAS_INTEGRATION_TESTS_ENABLED from deepdiff import DeepDiff import cript @@ -42,8 +44,10 @@ def integrate_nodes_helper(cript_api: cript.API, project_node: cript.Project): * ignoring the UID field through all the JSON because those the API changes when responding """ - # TODO remove skip test - return + if not HAS_INTEGRATION_TESTS_ENABLED: + pytest.skip("Integration tests with API requires real API and Storage token") + return + print("\n\n=================== Project Node ============================") print(project_node.get_json(sort_keys=False, condense_to_uuid={}, indent=2).json) print("==============================================================")