Skip to content

Commit 4ff9c0f

Browse files
Merge pull request #211 from microsoft/psl-unit-testing
test: Add unit tests for backend-api
2 parents 6b3dce7 + 3cd1758 commit 4ff9c0f

71 files changed

Lines changed: 13392 additions & 12 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/test.yml

Lines changed: 101 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ on:
99
- 'src/backend-api/**/*.py'
1010
- 'src/backend-api/pyproject.toml'
1111
- 'src/backend-api/pytest.ini'
12+
- 'src/processor/**/*.py'
13+
- 'src/processor/pyproject.toml'
1214
- '.github/workflows/test.yml'
1315
pull_request:
1416
types:
@@ -23,6 +25,8 @@ on:
2325
- 'src/backend-api/**/*.py'
2426
- 'src/backend-api/pyproject.toml'
2527
- 'src/backend-api/pytest.ini'
28+
- 'src/processor/**/*.py'
29+
- 'src/processor/pyproject.toml'
2630
- '.github/workflows/test.yml'
2731

2832
permissions:
@@ -36,10 +40,10 @@ jobs:
3640

3741
steps:
3842
- name: Checkout code
39-
uses: actions/checkout@v5
43+
uses: actions/checkout@v4
4044

4145
- name: Set up Python
42-
uses: actions/setup-python@v6
46+
uses: actions/setup-python@v5
4347
with:
4448
python-version: "3.12"
4549

@@ -48,7 +52,7 @@ jobs:
4852
python -m pip install --upgrade pip
4953
cd src/backend-api
5054
pip install -e .
51-
pip install pytest pytest-cov
55+
pip install pytest pytest-cov pytest-asyncio
5256
5357
- name: Check if Backend Test Files Exist
5458
id: check_backend_tests
@@ -71,9 +75,26 @@ jobs:
7175
--cov=src/app \
7276
--cov-report=term-missing \
7377
--cov-report=xml:reports/coverage.xml \
78+
--cov-fail-under=82 \
7479
--junitxml=pytest.xml \
7580
-v
7681
82+
- name: Prefix coverage XML filenames with repo-root path
83+
if: env.skip_backend_tests == 'false'
84+
run: |
85+
python <<'PY'
86+
import xml.etree.ElementTree as ET
87+
path = "src/backend-api/reports/coverage.xml"
88+
prefix = "src/backend-api/src/app/"
89+
tree = ET.parse(path)
90+
root = tree.getroot()
91+
for cls in root.iter("class"):
92+
fname = cls.attrib.get("filename", "")
93+
if fname and not fname.startswith(prefix):
94+
cls.attrib["filename"] = prefix + fname
95+
tree.write(path, xml_declaration=True, encoding="utf-8")
96+
PY
97+
7798
- name: Pytest Coverage Comment
7899
if: |
79100
always() &&
@@ -90,3 +111,80 @@ jobs:
90111
if: env.skip_backend_tests == 'true'
91112
run: |
92113
echo "Skipping backend tests because no test files were found."
114+
115+
processor_tests:
116+
runs-on: ubuntu-latest
117+
118+
steps:
119+
- name: Checkout code
120+
uses: actions/checkout@v4
121+
122+
- name: Set up Python
123+
uses: actions/setup-python@v5
124+
with:
125+
python-version: "3.12"
126+
127+
- name: Install Processor Dependencies
128+
run: |
129+
python -m pip install --upgrade pip
130+
cd src/processor
131+
pip install -e .
132+
pip install pytest pytest-cov pytest-asyncio
133+
134+
- name: Check if Processor Test Files Exist
135+
id: check_processor_tests
136+
run: |
137+
if [ -z "$(find src/processor/src/tests -type f -name 'test_*.py' 2>/dev/null)" ]; then
138+
echo "No processor test files found, skipping processor tests."
139+
echo "skip_processor_tests=true" >> $GITHUB_ENV
140+
else
141+
echo "Processor test files found, running tests."
142+
echo "skip_processor_tests=false" >> $GITHUB_ENV
143+
fi
144+
145+
- name: Run Processor Tests with Coverage
146+
if: env.skip_processor_tests == 'false'
147+
run: |
148+
cd src/processor
149+
pytest src/tests \
150+
--cov=src \
151+
--cov-report=term-missing \
152+
--cov-report=xml:reports/coverage.xml \
153+
--cov-fail-under=82 \
154+
--junitxml=pytest.xml \
155+
-v
156+
157+
- name: Prefix coverage XML filenames with repo-root path
158+
if: env.skip_processor_tests == 'false'
159+
run: |
160+
python <<'PY'
161+
import xml.etree.ElementTree as ET
162+
path = "src/processor/reports/coverage.xml"
163+
prefix = "src/processor/src/"
164+
tree = ET.parse(path)
165+
root = tree.getroot()
166+
for cls in root.iter("class"):
167+
fname = cls.attrib.get("filename", "")
168+
if fname and not fname.startswith(prefix):
169+
cls.attrib["filename"] = prefix + fname
170+
tree.write(path, xml_declaration=True, encoding="utf-8")
171+
PY
172+
173+
- name: Pytest Coverage Comment (Processor)
174+
if: |
175+
always() &&
176+
github.event_name == 'pull_request' &&
177+
github.event.pull_request.head.repo.fork == false &&
178+
env.skip_processor_tests == 'false'
179+
uses: MishaKav/pytest-coverage-comment@26f986d2599c288bb62f623d29c2da98609e9cd4 # v1.6.0
180+
with:
181+
pytest-xml-coverage-path: src/processor/reports/coverage.xml
182+
junitxml-path: src/processor/pytest.xml
183+
title: Processor Coverage Report
184+
unique-id-for-comment: processor
185+
report-only-changed-files: true
186+
187+
- name: Skip Processor Tests
188+
if: env.skip_processor_tests == 'true'
189+
run: |
190+
echo "Skipping processor tests because no test files were found."

src/backend-api/pyproject.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@ dependencies = [
2525
]
2626

2727
[dependency-groups]
28-
dev = ["pytest>=9.0.3", "pytest-cov>=6.2.1"]
28+
dev = ["pytest>=9.0.3", "pytest-cov>=6.2.1", "pytest-asyncio>=0.23.0"]
29+
30+
[tool.coverage.run]
31+
omit = ["src/tests/*"]
2932

3033
[tool.uv]
3134
override-dependencies = [
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
"""Tests for application.Application bootstrap."""
2+
3+
from application import Application
4+
from libs.base.typed_fastapi import TypedFastAPI
5+
from libs.services.interfaces import IDataService, IHttpService, ILoggerService
6+
from libs.services.process_services import ProcessService
7+
8+
9+
def test_application_initializes_typed_fastapi():
10+
app = Application()
11+
assert isinstance(app.app, TypedFastAPI)
12+
assert app.app.title == "FastAPI Application"
13+
assert app.app.version == "1.0.0"
14+
15+
16+
def test_application_sets_app_context_on_app():
17+
app = Application()
18+
assert app.app.app_context is app.application_context
19+
20+
21+
def test_application_registers_core_services():
22+
app = Application()
23+
ctx = app.application_context
24+
assert ctx.get_service(ILoggerService) is not None
25+
assert ctx.get_service(IHttpService) is not None
26+
assert ctx.get_service(IDataService) is not None
27+
assert ctx.get_service(ProcessService) is not None
28+
29+
30+
def test_application_includes_routers():
31+
app = Application()
32+
paths = {route.path for route in app.app.routes}
33+
# router_files
34+
assert "/api/file/upload" in paths
35+
# router_process
36+
assert "/api/process/create" in paths
37+
# http_probes
38+
assert "/health" in paths

0 commit comments

Comments
 (0)