Skip to content

Commit b5395ad

Browse files
authored
Merge pull request #76 from neo4j/snowflake-ci
Add integration testing for Snowflake notebooks
2 parents 2971668 + 844efb4 commit b5395ad

File tree

6 files changed

+104
-16
lines changed

6 files changed

+104
-16
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
name: Run Snowflake integration tests
2+
3+
# Controls when the workflow will run
4+
on:
5+
# Triggers the workflow on push or pull request events but only for the "main" branch
6+
push:
7+
branches: [ "main" ]
8+
# Skip on this check PR to minimize the load against Snowflake (and keep PR checks fast)
9+
10+
11+
# Allows you to run this workflow manually from the Actions tab
12+
workflow_dispatch:
13+
14+
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
15+
jobs:
16+
tests:
17+
# The type of runner that the job will run on
18+
runs-on: ${{ matrix.os}}
19+
timeout-minutes: 30
20+
strategy:
21+
fail-fast: false
22+
matrix:
23+
os: [ubuntu-latest]
24+
defaults:
25+
run:
26+
working-directory: python-wrapper
27+
28+
# Steps represent a sequence of tasks that will be executed as part of the job
29+
steps:
30+
- uses: actions/checkout@v4
31+
32+
- uses: actions/setup-python@v5
33+
with:
34+
python-version: "3.11"
35+
cache: 'pip'
36+
cache-dependency-path: pyproject.toml
37+
- run: pip install ".[dev]"
38+
- run: pip install ".[pandas]"
39+
40+
- name: Run tests
41+
env:
42+
SNOWFLAKE_ACCOUNT: ${{ secrets.SNOWFLAKE_ACCOUNT }}
43+
SNOWFLAKE_USER: ${{ secrets.SNOWFLAKE_USER }}
44+
SNOWFLAKE_PASSWORD: ${{ secrets.SNOWFLAKE_PASSWORD }}
45+
SNOWFLAKE_ROLE: ACCOUNTADMIN
46+
SNOWFLAKE_WAREHOUSE: ${{ secrets.SNOWFLAKE_WAREHOUSE }}
47+
run: pytest tests/ --include-snowflake

CONTRIBUTING.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ We can't guarantee that we'll accept pull requests and may ask you to make some
5050
Occasionally, we might also have logistical, commercial, or legal reasons why we can't accept your work but we'll try to find an alternative way for you to contribute in that case.
5151
Remember that many community members have become regular contributors and some are now even Neo employees!
5252

53+
5354
## Building the project locally
5455

5556
To build the Python packages, run inside the `python-wrapper` folder:
@@ -67,9 +68,13 @@ yarn build # Build JavaScript resources to be used by Python code
6768

6869
This will build the app and copy the relevant files to the python wrapper
6970

71+
7072
## Specifically for this project
7173

72-
Setting up the Python development environment:
74+
In this section, we will provide some more specific information about how to work with this particular project.
75+
76+
77+
### Python development environment
7378

7479
* Install Python 3.9+
7580
* [Install pip](https://pip.pypa.io/en/stable/installation/)
@@ -79,6 +84,28 @@ Setting up the Python development environment:
7984
pip install ".[dev]"
8085
```
8186

87+
### Testing
88+
89+
To run unit tests, execute:
90+
91+
```sh
92+
pytest python-wrapper/tests
93+
```
94+
95+
Additionally, there are integration tests that require an external data source.
96+
These require additional setup and configuration, such as environment variables specifying connection details.
97+
To run tests requiring a Neo4j DB instance with GDS installed, execute:
98+
99+
```sh
100+
pytest python-wrapper/tests --include-neo4j-and-gds
101+
```
102+
103+
To run tests requiring a Snowflake connection, execute:
104+
105+
```sh
106+
pytest python-wrapper/tests --include-snowflake
107+
```
108+
82109

83110
### Project structure
84111

python-wrapper/pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ notebook = [
6666
"ipywidgets>=8.0.0",
6767
"palettable==3.3.3",
6868
"matplotlib==3.10.0",
69+
"snowflake-snowpark-python==1.26.0",
6970
]
7071

7172
[project.urls]

python-wrapper/tests/conftest.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ def pytest_addoption(parser: Any) -> None:
1010
action="store_true",
1111
help="include tests requiring a Neo4j instance with GDS running",
1212
)
13+
parser.addoption(
14+
"--include-snowflake",
15+
action="store_true",
16+
help="include tests requiring a Snowflake connection",
17+
)
1318

1419

1520
def pytest_collection_modifyitems(config: Any, items: Any) -> None:
@@ -18,6 +23,11 @@ def pytest_collection_modifyitems(config: Any, items: Any) -> None:
1823
for item in items:
1924
if "requires_neo4j_and_gds" in item.keywords:
2025
item.add_marker(skip)
26+
if not config.getoption("--include-snowflake"):
27+
skip = pytest.mark.skip(reason="skipping since requiring a Snowflake connection")
28+
for item in items:
29+
if "requires_snowflake" in item.keywords:
30+
item.add_marker(skip)
2131

2232

2333
@pytest.fixture(scope="package")

python-wrapper/tests/pytest.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
[pytest]
22
markers =
33
requires_neo4j_and_gds: mark a test as a requiring a Neo4j instance with GDS running
4+
requires_snowflake: mark a test as a requiring a Snowflake connection
45
filterwarnings =
56
error
67
ignore:Jupyter is migrating its paths to use standard platformdirs:DeprecationWarning

python-wrapper/tests/test_notebooks.py

100755100644
Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -120,18 +120,20 @@ def filter_func(notebook: str) -> bool:
120120
run_notebooks(filter_func)
121121

122122

123-
# def test_snowflake() -> None:
124-
# snowflake_notebooks = ["snowflake-nvl-example.ipynb"]
125-
#
126-
# def filter_func(notebook: str) -> bool:
127-
# return notebook in snowflake_notebooks
128-
#
129-
# run_notebooks(filter_func)
130-
#
131-
# def test_simple() -> None:
132-
# simple_notebooks = ["simple-nvl-example.ipynb"]
133-
#
134-
# def filter_func(notebook: str) -> bool:
135-
# return notebook in simple_notebooks
136-
#
137-
# run_notebooks(filter_func)
123+
@pytest.mark.requires_snowflake
124+
def test_snowflake() -> None:
125+
snowflake_notebooks = ["snowpark-nvl-example.ipynb"]
126+
127+
def filter_func(notebook: str) -> bool:
128+
return notebook in snowflake_notebooks
129+
130+
run_notebooks(filter_func)
131+
132+
133+
def test_simple() -> None:
134+
simple_notebooks = ["simple-nvl-example.ipynb"]
135+
136+
def filter_func(notebook: str) -> bool:
137+
return notebook in simple_notebooks
138+
139+
run_notebooks(filter_func)

0 commit comments

Comments
 (0)