Skip to content

Commit 731338b

Browse files
test: add API integration suite, fixtures, and frontend unit tests (#40)
* test: add API integration suite, fixtures, and frontend unit tests * test: add integration and frontend test suite with search limit validation * test: address PR review — shared fixtures, coverage, hygiene Extract Flask client fixtures to tests/conftest.py, use client_single for search limit tests, and relax session count assertion to >= 1. Include utils/ in coverage (omit untested export modules), stop tracking .coverage, and align integration-tests CI with full cov scope. Co-Authored-By: Cursor <cursoragent@cursor.com> * test: address timon0305 PR review feedback Add client_empty and client_thinking fixtures, exercise thinking blocks via session detail API, preserve ValueError cause in _parse_limit, and remove .coverage from git tracking (already in .gitignore). Co-authored-by: Cursor <cursoragent@cursor.com> * Updating the assertion to be order-independent * test: collapse conftest fixtures and use real DOMPurify in JS tests Extract _make_test_client() for shared Flask client seeding. Run markdown XSS tests against dompurify/marked dev deps (CDN versions) instead of a regex mock sanitize implementation. --------- Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 7420358 commit 731338b

17 files changed

Lines changed: 3451 additions & 95 deletions

.github/workflows/ci.yml

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,14 @@ jobs:
1414
prod-install-smoke:
1515
name: Verify prod requirements.txt is sufficient
1616
runs-on: ubuntu-latest
17+
permissions:
18+
contents: read
1719
steps:
18-
- uses: actions/checkout@v4
20+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
21+
with:
22+
persist-credentials: false
1923

20-
- uses: actions/setup-python@v5
24+
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
2125
with:
2226
python-version: "3.12"
2327
cache: pip
@@ -41,10 +45,14 @@ jobs:
4145
4246
pytest:
4347
runs-on: ubuntu-latest
48+
permissions:
49+
contents: read
4450
steps:
45-
- uses: actions/checkout@v4
51+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
52+
with:
53+
persist-credentials: false
4654

47-
- uses: actions/setup-python@v5
55+
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
4856
with:
4957
python-version: "3.12"
5058
cache: pip
@@ -79,3 +87,52 @@ jobs:
7987

8088
- name: Run mypy on tests (non-strict smoke)
8189
run: mypy tests --config-file mypy-tests.ini --follow-imports skip
90+
91+
integration-tests:
92+
name: API integration tests + coverage
93+
runs-on: ubuntu-latest
94+
permissions:
95+
contents: read
96+
actions: write
97+
steps:
98+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
99+
with:
100+
persist-credentials: false
101+
102+
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
103+
with:
104+
python-version: "3.12"
105+
cache: pip
106+
cache-dependency-path: |
107+
requirements.txt
108+
requirements-dev.txt
109+
110+
- name: Install dev dependencies
111+
run: pip install -r requirements-dev.txt
112+
113+
# Subset run: skip fail-under (full suite enforces 60% in pytest job).
114+
- name: Run integration tests with coverage
115+
run: pytest tests/test_api_integration.py tests/test_search.py -v --cov=api --cov=utils --cov-report=xml --cov-fail-under=0
116+
117+
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
118+
with:
119+
name: coverage-report
120+
path: coverage.xml
121+
122+
js-tests:
123+
name: Frontend unit tests (vitest)
124+
runs-on: ubuntu-latest
125+
permissions:
126+
contents: read
127+
steps:
128+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
129+
with:
130+
persist-credentials: false
131+
132+
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
133+
with:
134+
node-version: "20"
135+
cache: npm
136+
137+
- run: npm ci
138+
- run: npm test

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,7 @@ build/
1010
.vscode/
1111
*.swp
1212
*.swo
13+
node_modules/
14+
.coverage
15+
coverage/
16+
coverage.xml

api/search.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,20 @@
1313
search_bp = Blueprint("search", __name__)
1414

1515
_DEFAULT_LIMIT = 50
16+
_MAX_LIMIT = 500
1617

1718

1819
def _parse_limit(raw: str | None, default: int = _DEFAULT_LIMIT) -> int:
1920
"""Parse a positive integer limit from a query string value."""
2021
if raw is None or raw.strip() == "":
2122
return default
2223
try:
23-
value = int(raw)
24-
except ValueError:
25-
raise ValueError("Invalid limit: must be a positive integer") from None
24+
value = int(raw.strip())
25+
except ValueError as exc:
26+
raise ValueError("Invalid limit: must be a positive integer") from exc
2627
if value < 1:
2728
raise ValueError("Invalid limit: must be a positive integer")
28-
return value
29+
return min(value, _MAX_LIMIT)
2930

3031

3132
@search_bp.route("/api/search")

0 commit comments

Comments
 (0)