Skip to content

Commit 9e6c1ef

Browse files
committed
Test library execution against Clickhouse engine
There has been a couple of recent issues in which test execution failed in certain scenarios (#48 and #53). These weren't caught by the libraries test as it would require executing actual tests against actual SQL queries. This commit will add a framework for running these tests against a running database engine (Clickhouse in the first instance) to better enable these kinds of bugs to be caught.
1 parent ac7d079 commit 9e6c1ef

File tree

6 files changed

+126
-8
lines changed

6 files changed

+126
-8
lines changed

.github/workflows/test-package.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ jobs:
1616
fail-fast: false
1717
matrix:
1818
python-version: ["3.9", "3.10", "3.11"]
19+
services:
20+
clickhouse:
21+
image: clickhouse/clickhouse-server:24.1.5.6
22+
ports:
23+
- 8123:8123
24+
- 9000:9000
1925

2026
steps:
2127
- uses: actions/checkout@v3
@@ -36,5 +42,9 @@ jobs:
3642
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
3743
poetry run flake8 . --count --exit-zero --max-complexity=10 --max-line-length=119 --statistics --ignore E203,E266,E501,W503
3844
- name: Test with pytest
45+
env:
46+
SQL_MOCK_CLICKHOUSE_HOST: 127.0.0.1
47+
SQL_MOCK_CLICKHOUSE_PORT: 8123
48+
SQL_MOCK_CLICKHOUSE_USER: default
3949
run: |
4050
poetry run pytest tests/

CONTRIBUTION.md

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,22 +45,42 @@ Once you have Poetry, you can install the project's dependencies:
4545
poetry install --all-extras
4646
```
4747

48-
### 3. Pre-Commit Hooks
48+
### 3. External Dependencies
4949

50-
This project uses pre-commit hooks to ensure code quality. To install the hooks, run:
50+
This project uses several external dependencies in the developer workflow. These are:
51+
52+
- [Pre-commit](https://pre-commit.com/) hooks are used to ensure code quality. Install the hooks with
53+
```
54+
poetry run pre-commit install
55+
```
56+
This will set up the necessary hooks to check code formatting, linting, and other code quality checks before each commit.
57+
- [Docker](https://www.docker.com/) and [Docker Compose](https://docs.docker.com/compose/) for running integrations tests (see below).
58+
- [Make](https://www.gnu.org/software/make/) is used as convenience wrapper around some of the more convoluted commands. This is optional as you can always run the commands directly (see the `Makefile` for the actual commands being run).
59+
60+
### 4. Running Tests
61+
62+
We use [pytest](https://docs.pytest.org/en/latest/) for running tests. You can run all the tests with:
5163
5264
```bash
53-
poetry run pre-commit install
65+
make test
5466
```
5567

56-
This will set up the necessary hooks to check code formatting, linting, and other code quality checks before each commit.
68+
There are two types of tests: those testing the internal functions and methods of the library and those testing the test execution with example queries. The latter require a running database instance to be available (either locally or remotely). Note, integration testing is currently only supported for Clickhouse. You can run internal testing only with:
5769

58-
### 4. Running Tests
70+
```bash
71+
make test-unit
72+
```
5973

60-
We use [pytest](https://docs.pytest.org/en/latest/) for running tests. You can run all the tests with:
74+
Running integration tests locally requires [Docker](https://www.docker.com/) and [Docker Compose](https://docs.docker.com/compose/). First, start the local database services with:
75+
76+
```bash
77+
docker compose up -d
78+
```
79+
80+
Then you can run integration tests with:
6181

6282
```bash
63-
poetry run pytest tests/
83+
make test-integration
6484
```
6585

6686
### 5. Environment Variables

Makefile

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
.DEFAULT_GOAL := help
2+
SHELL := /bin/bash
3+
4+
.PHONY: help
5+
help: ## Show all available commands
6+
@awk 'BEGIN {FS = ":.*##"; printf "Usage: make \033[36m<target>\033[0m\n"} /^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-13s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST);
7+
8+
.PHONY: test
9+
test: ## Run test pipeline
10+
poetry run pytest tests/
11+
12+
.PHONY: test-integration
13+
test-integration: ## Run integration tests
14+
poetry run pytest -m "integration" tests/
15+
16+
.PHONY: test-unit
17+
test-unit: ## Run unit tests
18+
poetry run pytest -m "not integration" tests/

compose.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
services:
2+
clickhouse:
3+
image: clickhouse/clickhouse-server:24.1.5.6
4+
ports:
5+
- 8123:8123
6+
- 9000:9000
7+
ulimits:
8+
nofile:
9+
soft: "262144"
10+
hard: "262144"

pyproject.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,12 @@ flake8 = "^6.1.0"
7575
requires = ["poetry-core>=1.0.0"]
7676
build-backend = "poetry.core.masonry.api"
7777

78-
# Ignore Google Bigquery namespace deprecation warnings
7978
[tool.pytest.ini_options]
79+
# Ignore Google Bigquery namespace deprecation warnings
8080
filterwarnings = [
8181
"ignore:Deprecated call to `pkg_resources\\.declare_namespace\\('.*'\\):DeprecationWarning",
8282
"ignore::DeprecationWarning:google.rpc",
8383
]
84+
markers = [
85+
"integration: Integration tests requiring running database instances"
86+
]
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import os
2+
3+
import pytest
4+
5+
from sql_mock.clickhouse import column_mocks as col
6+
from sql_mock.clickhouse.table_mocks import ClickHouseTableMock
7+
from sql_mock.table_mocks import table_meta
8+
9+
pytestmark = pytest.mark.integration
10+
11+
12+
@pytest.fixture(autouse=True)
13+
def set_env():
14+
if not os.getenv("SQL_MOCK_CLICKHOUSE_HOST"):
15+
os.environ["SQL_MOCK_CLICKHOUSE_HOST"] = "localhost"
16+
if not os.getenv("SQL_MOCK_CLICKHOUSE_PORT"):
17+
os.environ["SQL_MOCK_CLICKHOUSE_PORT"] = "8123"
18+
if not os.getenv("SQL_MOCK_CLICKHOUSE_USER"):
19+
os.environ["SQL_MOCK_CLICKHOUSE_USER"] = "default"
20+
if not os.getenv("SQL_MOCK_CLICKHOUSE_PASSWORD"):
21+
os.environ["SQL_MOCK_CLICKHOUSE_PASSWORD"] = ""
22+
23+
24+
def test_simple_query():
25+
query = """SELECT
26+
user_id,
27+
count() AS sessions
28+
FROM sessions
29+
GROUP BY user_id
30+
"""
31+
32+
@table_meta(table_ref="sessions")
33+
class SessionsMock(ClickHouseTableMock):
34+
user_id = col.String(default="foo")
35+
36+
@table_meta(query=query)
37+
class ResultMock(ClickHouseTableMock):
38+
user_id = col.String(default="foo")
39+
sessions = col.Int(default=0)
40+
41+
sessions_mock = SessionsMock.from_dicts(
42+
[
43+
{"user_id": "a"},
44+
{"user_id": "a"},
45+
{"user_id": "a"},
46+
{"user_id": "b"},
47+
],
48+
)
49+
50+
result = ResultMock.from_mocks(input_data=[sessions_mock])
51+
52+
expected = [
53+
{"user_id": "a", "sessions": 3},
54+
{"user_id": "b", "sessions": 1},
55+
]
56+
57+
result.assert_equal(expected)

0 commit comments

Comments
 (0)