Skip to content

Commit 9c1a5e3

Browse files
authored
test: add integration tests (#11)
1 parent 8784b5c commit 9c1a5e3

21 files changed

+357
-91
lines changed

.containerignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
!share
2+
*

.github/workflows/ci.yaml

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,25 @@ jobs:
1212
runs-on: ubuntu-latest
1313
steps:
1414
- uses: actions/checkout@v3
15+
- name: Install uv
16+
uses: astral-sh/setup-uv@v5
1517
- uses: actions/setup-python@v3
18+
- name: Set up pip cache
19+
if: runner.os == 'Linux'
20+
uses: actions/cache@v3
21+
with:
22+
path: ~/.cache/pip
23+
key: ${{ runner.os }}-pip-${{ hashFiles('pyproject.toml') }}
24+
restore-keys: ${{ runner.os }}-pip-
25+
- name: Install Hatch
26+
run: pipx install hatch
1627
- uses: pre-commit/[email protected]
28+
with:
29+
extra_args: --color=always
1730

1831
test:
1932
name: "tests & coverage 💊"
20-
needs: precommit
33+
# needs: precommit
2134
runs-on: ubuntu-latest
2235
strategy:
2336
matrix:

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ venv.bak/
4444
######################
4545
*.log
4646
*.log.*
47-
*.sql
4847
*.sqlite
4948

5049
# OS generated files #

.pre-commit-config.yaml

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@ repos:
1515
- id: typos
1616
stages: [commit]
1717
exclude: "^typings/.*"
18-
- repo: https://github.com/pre-commit/mirrors-mypy
19-
rev: v1.10.1 # Use the sha / tag you want to point at
18+
- repo: local
2019
hooks:
21-
- id: mypy
22-
language: system
23-
pass_filenames: false
24-
args: ['.']
20+
- id: mypy-hatch
21+
name: mypy-hatch
22+
language: system
23+
pass_filenames: false
24+
args: ['.']
25+
entry: hatch
26+
args: ["run", "types:check"]
2527
- repo: https://github.com/compilerla/conventional-pre-commit
2628
rev: v3.3.0
2729
hooks:

CONTRIBUTING.md

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,22 @@ Install hatch with `pipx` and configure it. As a minimum, configure it to create
1010
[dirs.env]
1111
virtual = ".venv"
1212
```
13+
# Cheat sheet
14+
- tests: `hatch run test:test`
15+
- docs: `hatch run dev:docs`
1316

14-
# VSCode
17+
# Dev tooling
18+
## VSCode
1519
1. Clone the repo
1620
1. Run `hatch env create && hatch env create dev && hatch env create test`
1721
1. Open VSCode `code .`
1822
1. Select the `dev` environment for development [See the documentation.](https://hatch.pypa.io/latest/how-to/integrate/vscode/)
1923

2024
To enable test discovery and test debugging, change the *python interpreter path* to a test environments path e.g. `test.py3.11`.
2125

22-
# No VSCode
26+
## No VSCode
2327
I suggest NeoVim or Zed. See the cheat-sheet below.
2428

25-
# Cheat sheet
26-
- tests: `hatch test`
27-
- docs: `hatch run dev:docs`
2829

2930
# Documentation
3031
The docs is built into "sites" folder. This is gitignored and the docs is built in CI.
@@ -54,19 +55,28 @@ The package follows semantic versioning. Breaking changes will occur unannounced
5455

5556

5657
# Docker
57-
The base Dockerfile can be used to run Airflow and install dagcellent in _editable_ mode, so it gives you a short feedback loop.
58+
It is recommended to use Podman with the container files. The base Dockerfile can be used to run Airflow and install dagcellent in _editable_ mode, so it gives you a short feedback loop.
59+
60+
# Tests
61+
The testing suite uses Pytest.
5862

59-
# tests
6063
## Unit tests
6164
### Fuzzing/hypothesis
65+
*coming soon* 👀
6266

6367
## Integration tests
64-
The CI will run integration tests, where external components are not mocked, but real containerized entities are used.
68+
The CI will run integration tests, where external components are not mocked, but real containerized entities are used. All integration tests are marked with `integration`.
69+
70+
In general, prefer integration testing/system tests over mocking. E.g.: to guarantee that our tools work on various SQL engines, implement integration tests against those engines.
6571

72+
The following structure illustrates where to find the various integration test.
73+
```
74+
tests
75+
├── dags
76+
└── integration
77+
├── mlflow
78+
├── mssql
79+
└── psql
80+
```
6681

67-
The following integrations are available (docker commands should be executed from the project root folder):
68-
- mssql: `docker compose -f docker-compose.yaml -f ./tests/integration/docker-compose.override.mssql.yaml up --detach`
69-
- psql: `docker compose -f docker-compose.yaml -f ./tests/integration/docker-compose.override.psql.yaml up --detach`
7082

71-
To stop the running instances, it is a good idea to use the `volumes` flag to remove persistent data:
72-
`docker compose -f docker-compose.yaml -f ./tests/integration/docker-compose.override.mssql.yaml down`

docker-compose.yaml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,11 @@ version: '3.8'
99
services:
1010
airflow:
1111
build: .
12+
#image: pytest74406-airflow:latest
1213
ports:
1314
- "8080:8080"
1415
container_name: airflow
1516
restart: always
16-
depends_on:
17-
- db
1817
volumes:
1918
- ./tests/dags:/opt/airflow/dags
2019
- ./:/opt/dagcellent/

pyproject.toml

Lines changed: 15 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
[build-system]
2-
requires = ["hatchling", "versioningit"]
2+
requires = ["hatchling"]
33
build-backend = "hatchling.build"
44

55
[project]
66
name = "dagcellent"
7-
dynamic = ["version"]
7+
version = "0.2.0"
88
description = ''
99
readme = "README.md"
1010
requires-python = ">=3.11"
@@ -28,7 +28,7 @@ classifiers = [
2828
dependencies = [
2929
"apache-airflow>=2.9.1",
3030
"apache-airflow-providers-amazon>=7.2.0",
31-
"apache-airflow-providers-microsoft-mssql >=3.2.0",
31+
"apache-airflow-providers-microsoft-mssql >=4.0.0",
3232
"apache-airflow-providers-snowflake >= 5.1",
3333
"apache-airflow-providers-common-sql[pandas,openlineage]",
3434
"tomli >= 2.0.1",
@@ -37,7 +37,9 @@ dependencies = [
3737
"pandas > 2.0.0",
3838
"awswrangler[redshift, postgres, sqlserver, deltalake] > 3.0.0",
3939
"pyarrow >= 17.0.0",
40-
"mlflow-skinny >= 2.17.2"
40+
"mlflow-skinny >= 2.17.2",
41+
# TODO: https://github.com/xmlsec/python-xmlsec/issues/345
42+
"xmlsec != 1.3.15"
4143
]
4244

4345
[project.urls]
@@ -64,18 +66,17 @@ extra-dependencies = [
6466
"pre-commit == 3.7.*",
6567
"ruff == 0.4.4",
6668
"mypy == 1.10.*",
67-
"versioningit == 3.1.*",
6869
"towncrier == 23.11.*",
6970
]
7071

7172
[tool.hatch.envs.dev.scripts]
7273
install = "pre-commit install --hook-type commit-msg"
73-
version = "versioningit"
7474
changelog = "git-cliff -o CHANGELOG.md"
7575

7676
[tool.hatch.envs.types]
7777
extra-dependencies = [
7878
"mypy>=1.0.0",
79+
"types-requests",
7980
]
8081
[tool.hatch.envs.types.scripts]
8182
check = "mypy --install-types --non-interactive {args:src/dagcellent tests}"
@@ -96,16 +97,21 @@ extra-dependencies = [
9697
"pytest >= 8.0.0",
9798
"pytest-cov",
9899
"pytest-mock >= 3.14.0",
100+
"pytest-docker",
99101
]
100102

101103
[[tool.hatch.envs.test.matrix]]
102104
python = ["3.11", "3.12"]
103105

104106
[tool.hatch.envs.test.scripts]
105-
test = "pytest --cov=dagcellent --cov-report=term-missing --cov-report=xml --cov-report=html tests"
107+
test = "pytest --cov=dagcellent --cov-report=term-missing --cov-report=xml --cov-report=html --container-scope module tests"
108+
109+
[tool.pytest.ini_options]
110+
markers = [
111+
"integration: Run tests against containers.",
112+
"mlflow: Run MLFlow tests."
113+
]
106114

107-
[tool.hatch.version]
108-
source = "versioningit"
109115

110116
[tool.coverage.run]
111117
source_pkgs = ["dagcellent", "tests"]
@@ -311,24 +317,4 @@ exclude = [
311317
"^typings/*"
312318
]
313319

314-
[tool.versioningit]
315-
default-version = "0.0.0-unknown"
316-
317-
[tool.versioningit.vcs]
318-
# The method key:
319-
method = "git" # <- The method name
320-
321-
# Parameters to pass to the method:
322-
match = ["v*"]
323-
default-tag = "0.0.1"
324-
325-
[tool.hatch.build.hooks.versioningit-onbuild]
326-
source-file = "src/dagcellent/__init__.py"
327-
build-file = "dagcellent/__init__.py"
328-
require-match = true
329-
330-
[tool.versioningit.format]
331-
distance = "{next_version}.dev{distance}+{vcs}{rev}"
332-
dirty = "{version}+dirty"
333-
distance-dirty = "{next_version}.dev{distance}+{vcs}{rev}.dirty"
334320

src/dagcellent/dag/_dynamic.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
from __future__ import annotations
44

55
import json
6-
from collections.abc import Callable, Iterable
7-
from pathlib import Path
8-
from typing import TypeVar
6+
from typing import TYPE_CHECKING, TypeVar
97

108
import tomli
119
from pydantic import BaseModel, ConfigDict
1210

11+
if TYPE_CHECKING:
12+
from collections.abc import Callable, Iterable
13+
from pathlib import Path
14+
1315
T = TypeVar("T", bound="Config")
1416

1517

src/dagcellent/data_utils/sql_reflection.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ def safe_add_database_to_connection(engine: Engine, database: str) -> Engine:
151151
def reflect_meta_data(engine: Engine, schema: str | None, table: str) -> Table | None:
152152
"""Reflects the metadata from the engine."""
153153
meta_data = MetaData(schema=schema)
154-
return Table(table, meta_data, autoload_with=engine)
154+
return Table(table, meta_data, autoload_with=engine, schema=schema)
155155

156156

157157
def _log_reflected_table(meta_data: MetaData, table_name: str) -> None:

src/dagcellent/operators/external_table_arrow.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from __future__ import annotations
22

3-
from collections.abc import Sequence
4-
from typing import Any
3+
from typing import TYPE_CHECKING, Any
54

65
from airflow.models.baseoperator import BaseOperator
76

@@ -10,6 +9,9 @@
109
create_external_table_redshift_arrow,
1110
)
1211

12+
if TYPE_CHECKING:
13+
from collections.abc import Sequence
14+
1315

1416
class CreateExternalTableArrow(BaseOperator):
1517
"""Generate SQL DDL to create external table in target database."""

0 commit comments

Comments
 (0)