From 18f6663911cc3e97299182813a280f9327a44de9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florentin=20D=C3=B6rre?= Date: Fri, 3 Jan 2025 15:10:19 +0100 Subject: [PATCH 1/6] Ignore env files Co-authored-by: Adam Schill Collberg --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 0ec375d5..985b3507 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # Environments .env +*.env .venv env/ venv/ From 8ef8c9311ffcc9bbd8dfb1fa1b1a2f710bc3b72a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florentin=20D=C3=B6rre?= Date: Fri, 3 Jan 2025 15:10:50 +0100 Subject: [PATCH 2/6] Setup fixture for GDS connection --- python-wrapper/tests/conftest.py | 28 ++++++++++++++++++- python-wrapper/tests/gds_helper.py | 44 ++++++++++++++++++++++++++++++ python-wrapper/tests/test_gds.py | 19 ++----------- 3 files changed, 74 insertions(+), 17 deletions(-) create mode 100644 python-wrapper/tests/gds_helper.py diff --git a/python-wrapper/tests/conftest.py b/python-wrapper/tests/conftest.py index 92152dbf..8993d32e 100644 --- a/python-wrapper/tests/conftest.py +++ b/python-wrapper/tests/conftest.py @@ -1,4 +1,5 @@ -from typing import Any +import os +from typing import Any, Generator import pytest @@ -17,3 +18,28 @@ def pytest_collection_modifyitems(config: Any, items: Any) -> None: for item in items: if "requires_neo4j_and_gds" in item.keywords: item.add_marker(skip) + + +@pytest.fixture(scope="package") +def gds() -> Generator[Any, None, None]: + from gds_helper import aura_api, connect_to_plugin_gds, create_aurads_instance + from graphdatascience import GraphDataScience + + NEO4J_URI = os.environ.get("NEO4J_URI") + + if NEO4J_URI: + gds = connect_to_plugin_gds(NEO4J_URI) + yield gds + gds.close() + else: + api = aura_api() + id, dbms_connection_info = create_aurads_instance(api) + yield GraphDataScience( + endpoint=dbms_connection_info.uri, + auth=(dbms_connection_info.username, dbms_connection_info.password), + aura_ds=True, + database="neo4j", + arrow=False, + ) + + api.delete_instance(id) diff --git a/python-wrapper/tests/gds_helper.py b/python-wrapper/tests/gds_helper.py new file mode 100644 index 00000000..6b1dff85 --- /dev/null +++ b/python-wrapper/tests/gds_helper.py @@ -0,0 +1,44 @@ +import os + +from graphdatascience import GraphDataScience +from graphdatascience.session import DbmsConnectionInfo, SessionMemory +from graphdatascience.session.aura_api import AuraApi +from graphdatascience.session.aura_api_responses import InstanceCreateDetails + + +def connect_to_plugin_gds(uri: str) -> GraphDataScience: + NEO4J_AUTH = ("neo4j", "password") + if os.environ.get("NEO4J_USER"): + NEO4J_AUTH = (os.environ.get("NEO4J_USER", "DUMMY"), os.environ.get("NEO4J_PASSWORD", "neo4j")) + + return GraphDataScience(endpoint=uri, auth=NEO4J_AUTH, database="neo4j", arrow=False) + + +def aura_api() -> AuraApi: + return AuraApi( + client_id=os.environ["AURA_API_CLIENT_ID"], + client_secret=os.environ["AURA_API_CLIENT_SECRET"], + tenant_id=os.environ.get("AURA_API_TENANT_ID"), + ) + + +def create_aurads_instance(api: AuraApi) -> tuple[str, DbmsConnectionInfo]: + # Switch to Sessions once they can be created without a DB + instance_details: InstanceCreateDetails = api.create_instance( + name="ci-neo4j-viz-session", + memory=SessionMemory.m_8GB.value, + cloud_provider="gcp", + region="europe-west1", + ) + + wait_result = api.wait_for_instance_running(instance_id=instance_details.id) + if wait_result.error: + raise Exception(f"Error while waiting for instance to be running: {wait_result.error}") + + wait_result.connection_url + + return instance_details.id, DbmsConnectionInfo( + uri=wait_result.connection_url, + username="neo4j", + password=instance_details.password, + ) diff --git a/python-wrapper/tests/test_gds.py b/python-wrapper/tests/test_gds.py index 6544ec3c..1c504985 100644 --- a/python-wrapper/tests/test_gds.py +++ b/python-wrapper/tests/test_gds.py @@ -1,5 +1,5 @@ -import os import sys +from typing import Any import pandas as pd import pytest @@ -7,21 +7,10 @@ from neo4j_viz import Node -NEO4J_URI = os.environ.get("NEO4J_URI", "bolt://localhost:7687") - -NEO4J_AUTH = ("neo4j", "password") -if os.environ.get("NEO4J_USER"): - NEO4J_AUTH = ( - os.environ.get("NEO4J_USER", "DUMMY"), - os.environ.get("NEO4J_PASSWORD", "neo4j"), - ) - @pytest.mark.skipif(sys.version_info >= (3, 13), reason="requires python 3.12 or lower") @pytest.mark.requires_neo4j_and_gds -def test_from_gds_integration() -> None: - from graphdatascience import GraphDataScience - +def test_from_gds_integration(gds: Any) -> None: from neo4j_viz.gds import from_gds nodes = pd.DataFrame( @@ -40,10 +29,8 @@ def test_from_gds_integration() -> None: } ) - gds = GraphDataScience(NEO4J_URI, auth=NEO4J_AUTH) - with gds.graph.construct("flo", nodes, rels) as G: - VG = from_gds(gds, G, size_property="score", additional_node_properties=["component"]) + VG = from_gds(gds, G, size_property="score", additional_node_properties=["component"], node_radius_min_max=(3.14, 1337)) assert len(VG.nodes) == 3 assert sorted(VG.nodes, key=lambda x: x.id) == [ From 010786737b459ae4d37b90cd0096c7f59dce7a76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florentin=20D=C3=B6rre?= Date: Fri, 3 Jan 2025 15:13:47 +0100 Subject: [PATCH 3/6] Fix GDS intregation test for arrow Co-authored-by: Adam Schill Collberg --- python-wrapper/tests/conftest.py | 1 - python-wrapper/tests/gds_helper.py | 2 +- python-wrapper/tests/test_gds.py | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/python-wrapper/tests/conftest.py b/python-wrapper/tests/conftest.py index 8993d32e..ce04da17 100644 --- a/python-wrapper/tests/conftest.py +++ b/python-wrapper/tests/conftest.py @@ -39,7 +39,6 @@ def gds() -> Generator[Any, None, None]: auth=(dbms_connection_info.username, dbms_connection_info.password), aura_ds=True, database="neo4j", - arrow=False, ) api.delete_instance(id) diff --git a/python-wrapper/tests/gds_helper.py b/python-wrapper/tests/gds_helper.py index 6b1dff85..a12c7c12 100644 --- a/python-wrapper/tests/gds_helper.py +++ b/python-wrapper/tests/gds_helper.py @@ -11,7 +11,7 @@ def connect_to_plugin_gds(uri: str) -> GraphDataScience: if os.environ.get("NEO4J_USER"): NEO4J_AUTH = (os.environ.get("NEO4J_USER", "DUMMY"), os.environ.get("NEO4J_PASSWORD", "neo4j")) - return GraphDataScience(endpoint=uri, auth=NEO4J_AUTH, database="neo4j", arrow=False) + return GraphDataScience(endpoint=uri, auth=NEO4J_AUTH, database="neo4j") def aura_api() -> AuraApi: diff --git a/python-wrapper/tests/test_gds.py b/python-wrapper/tests/test_gds.py index 1c504985..577a6055 100644 --- a/python-wrapper/tests/test_gds.py +++ b/python-wrapper/tests/test_gds.py @@ -16,7 +16,7 @@ def test_from_gds_integration(gds: Any) -> None: nodes = pd.DataFrame( { "nodeId": [0, 1, 2], - "labels": ["A", "C", ["A", "B"]], + "labels": [["A"], ["C"], ["A", "B"]], "score": [1337, 42, 3.14], "component": [1, 4, 2], } From 1527a86875c7e8dfe7147e5bff39bead9e704cf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florentin=20D=C3=B6rre?= Date: Fri, 3 Jan 2025 16:07:29 +0100 Subject: [PATCH 4/6] Add GDS integration test workflow --- .github/workflows/gds-integration-tests.yml | 45 +++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 .github/workflows/gds-integration-tests.yml diff --git a/.github/workflows/gds-integration-tests.yml b/.github/workflows/gds-integration-tests.yml new file mode 100644 index 00000000..c6362f3b --- /dev/null +++ b/.github/workflows/gds-integration-tests.yml @@ -0,0 +1,45 @@ +name: Run GDS integration tests + +# Controls when the workflow will run +on: + # Triggers the workflow on push or pull request events but only for the "main" branch + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + tests: + # The type of runner that the job will run on + runs-on: ${{ matrix.os}} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + defaults: + run: + working-directory: python-wrapper + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + cache: 'pip' + cache-dependency-path: pyproject.toml + - run: pip install ".[dev]" + - run: pip install ".[pandas]" + - run: pip install ".[gds]" + + - name: Run tests + env: + AURA_API_CLIENT_ID: 4V1HYCYEeoU4dSxThKnBeLvE2U4hSphx + AURA_API_CLIENT_SECRET: ${{ secrets.AURA_API_CLIENT_SECRET }} + AURA_API_TENANT_ID: eee7ec28-6b1a-5286-8e3a-3362cc1c4c78 + run: pytest tests/ --include-neo4j-and-gds -Werror From 2b252b8d6445dae34fa986b67016d100033bcfa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florentin=20D=C3=B6rre?= Date: Fri, 3 Jan 2025 16:12:36 +0100 Subject: [PATCH 5/6] Also trigger tests + code style on test changes --- .github/workflows/code-style.yml | 2 +- .github/workflows/gds-integration-tests.yml | 3 +-- .github/workflows/unit-tests.yml | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/code-style.yml b/.github/workflows/code-style.yml index cc8a76fb..9acfd98e 100644 --- a/.github/workflows/code-style.yml +++ b/.github/workflows/code-style.yml @@ -7,7 +7,7 @@ on: branches: [ "main" ] pull_request: paths: - - "python-wrapper/src/neo4j_viz/**" # python code + its resources + - "python-wrapper/**" # python code + its resources branches: [ "main" ] # Allows you to run this workflow manually from the Actions tab diff --git a/.github/workflows/gds-integration-tests.yml b/.github/workflows/gds-integration-tests.yml index c6362f3b..10ed510f 100644 --- a/.github/workflows/gds-integration-tests.yml +++ b/.github/workflows/gds-integration-tests.yml @@ -5,8 +5,7 @@ on: # Triggers the workflow on push or pull request events but only for the "main" branch push: branches: [ "main" ] - pull_request: - branches: [ "main" ] + # Skip on this check PR to reduce number of AuraDS instances created # Allows you to run this workflow manually from the Actions tab workflow_dispatch: diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 4cb5bbb1..b65329b1 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -7,7 +7,7 @@ on: branches: [ "main" ] pull_request: paths: - - "python-wrapper/src/neo4j_viz/**" # python code + its resources + - "python-wrapper/**" # python code + its resources - "python-wrapper/pyproject.toml" # dependencies branches: [ "main" ] From 7146a4ce94bf79c00234e24dd64a8ffac4c64ed4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florentin=20D=C3=B6rre?= Date: Fri, 3 Jan 2025 16:12:49 +0100 Subject: [PATCH 6/6] Format code --- python-wrapper/tests/test_gds.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/python-wrapper/tests/test_gds.py b/python-wrapper/tests/test_gds.py index 577a6055..d6a506de 100644 --- a/python-wrapper/tests/test_gds.py +++ b/python-wrapper/tests/test_gds.py @@ -30,7 +30,9 @@ def test_from_gds_integration(gds: Any) -> None: ) with gds.graph.construct("flo", nodes, rels) as G: - VG = from_gds(gds, G, size_property="score", additional_node_properties=["component"], node_radius_min_max=(3.14, 1337)) + VG = from_gds( + gds, G, size_property="score", additional_node_properties=["component"], node_radius_min_max=(3.14, 1337) + ) assert len(VG.nodes) == 3 assert sorted(VG.nodes, key=lambda x: x.id) == [