Skip to content

Commit 20592d1

Browse files
committed
ci: split unit and integration tests
Closes #1402
1 parent 435a63f commit 20592d1

File tree

4 files changed

+112
-21
lines changed

4 files changed

+112
-21
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
name: Shared setup backend
2+
runs:
3+
using: "composite"
4+
steps:
5+
- name: Set up Python
6+
uses: actions/setup-python@v4
7+
with:
8+
python-version: '3.12'
9+
10+
- name: Install Poetry
11+
run: |
12+
python -m pip install --upgrade pip
13+
pip install poetry
14+
shell: bash
15+
16+
- name: Install dependencies
17+
working-directory: ./backend
18+
run: poetry install
19+
shell: bash

.github/workflows/ci.yaml

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -104,27 +104,15 @@ jobs:
104104
run: pnpm build
105105
working-directory: ./dashboard
106106

107-
build-django:
107+
lint-and-unit-test-django:
108108
if: github.event.pull_request.draft != true
109109
runs-on: ubuntu-latest
110-
timeout-minutes: 10
110+
timeout-minutes: 3
111111
steps:
112-
- name: Checkout code
113-
uses: actions/checkout@v4
114-
115-
- name: Set up Python
116-
uses: actions/setup-python@v4
117-
with:
118-
python-version: '3.12'
119-
120-
- name: Install Poetry
121-
run: |
122-
python -m pip install --upgrade pip
123-
pip install poetry
112+
- uses: actions/checkout@v4
124113

125-
- name: Install dependencies
126-
run: poetry install
127-
working-directory: ./backend
114+
- name: Setup Backend
115+
uses: ./.github/actions/setup-backend
128116

129117
- name: Lint
130118
run: poetry run flake8
@@ -134,6 +122,20 @@ jobs:
134122
run: poetry run black --check .
135123
working-directory: ./backend
136124

125+
- name: Run unit tests
126+
run: poetry run pytest -m unit
127+
working-directory: ./backend
128+
129+
integration-test-django:
130+
if: github.event.pull_request.draft != true
131+
runs-on: ubuntu-latest
132+
timeout-minutes: 10
133+
steps:
134+
- uses: actions/checkout@v4
135+
136+
- name: Setup Backend
137+
uses: ./.github/actions/setup-backend
138+
137139
- name: Configure credentials variables
138140
run: |
139141
echo -n "${{ secrets.PG_JSON }}" | base64 --decode > application_default_credentials.json
@@ -163,11 +165,11 @@ jobs:
163165
break
164166
fi
165167
echo "Waiting for backend to be ready... $i"
166-
sleep 10
168+
sleep 5
167169
done
168170
169-
- name: Run tests
170-
run: poetry run pytest --run-all
171+
- name: Run integration tests
172+
run: poetry run pytest -m "integration" --run-all
171173
working-directory: ./backend
172174

173175
- name: Clean containers
@@ -179,7 +181,8 @@ jobs:
179181
needs:
180182
- lint-js
181183
- build-front
182-
- build-django
184+
- lint-and-unit-test-django
185+
- integration-test-django
183186
steps:
184187
- name: Configure staging host authenticity
185188
run: |

backend/conftest.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,69 @@
1+
import tomllib
2+
from pytest import Item
3+
import os
4+
5+
16
def pytest_addoption(parser):
27
parser.addoption(
38
"--run-all", action="store_true", default=False, help="run all test cases"
49
)
10+
11+
12+
def get_test_markers() -> list[str]:
13+
try:
14+
pyproject_path = os.path.join(os.path.dirname(__file__), "pyproject.toml")
15+
16+
with open(pyproject_path, "rb") as f:
17+
pyproject_data = tomllib.load(f)
18+
19+
markers: list[str] = (
20+
pyproject_data.get("tool", {})
21+
.get("pytest", {})
22+
.get("ini_options", {})
23+
.get("markers", [])
24+
)
25+
26+
marker_names = []
27+
for marker in markers:
28+
if ":" in marker:
29+
marker_name = marker.split(":")[0].strip()
30+
marker_names.append(marker_name)
31+
32+
if not marker_names:
33+
raise ValueError("No markers found in pyproject.toml")
34+
35+
return marker_names
36+
37+
except FileNotFoundError:
38+
raise FileNotFoundError("pyproject.toml not found")
39+
except Exception as e:
40+
raise e(f"Error reading markers from pyproject.toml: {e}")
41+
42+
43+
def pytest_collection_modifyitems(items: list[Item]):
44+
test_markers = get_test_markers()
45+
46+
for item in items:
47+
parent_folder = item.path.parent.name.lower()
48+
49+
matched_markers = [marker for marker in test_markers if marker in parent_folder]
50+
match len(matched_markers):
51+
case 0:
52+
raise Exception(
53+
"""Test %s must have a type.
54+
Place the test in a folder whose name contains 'unit' or 'integration'."""
55+
% item.name
56+
)
57+
case 1:
58+
item.add_marker(matched_markers[0])
59+
case _:
60+
raise Exception(
61+
"""Test %s can only be of a single type (one of %s),
62+
but found multiple markers (%s) at folder %s"""
63+
% (
64+
item.name,
65+
test_markers,
66+
matched_markers,
67+
parent_folder,
68+
)
69+
)

backend/pyproject.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ pytest-rerunfailures = "^15.0"
3636
[tool.pytest.ini_options]
3737
DJANGO_SETTINGS_MODULE = "kernelCI.settings"
3838
addopts = "-n 4 --dist loadscope --reruns 4 -x"
39+
markers = [
40+
"unit: marks tests as unit tests (fast, no external dependencies)",
41+
"integration: marks tests as integration tests (slow, connects to the database)"
42+
]
3943

4044
[build-system]
4145
requires = ["poetry-core"]

0 commit comments

Comments
 (0)