Skip to content

Commit 650a4a9

Browse files
test: add API integration suite, fixtures, and frontend unit tests
1 parent 4bbb456 commit 650a4a9

17 files changed

Lines changed: 3341 additions & 1 deletion

.coverage

52 KB
Binary file not shown.

.github/workflows/ci.yml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,42 @@ jobs:
5757

5858
- name: Run tests
5959
run: pytest --tb=short -q
60+
61+
integration-tests:
62+
name: API integration tests + coverage
63+
runs-on: ubuntu-latest
64+
steps:
65+
- uses: actions/checkout@v4
66+
67+
- uses: actions/setup-python@v5
68+
with:
69+
python-version: "3.12"
70+
cache: pip
71+
cache-dependency-path: |
72+
requirements.txt
73+
requirements-dev.txt
74+
75+
- name: Install dev dependencies
76+
run: pip install -r requirements-dev.txt
77+
78+
- name: Run integration tests with coverage
79+
run: pytest tests/test_api_integration.py tests/test_search.py -v --cov=api --cov-report=xml
80+
81+
- uses: actions/upload-artifact@v4
82+
with:
83+
name: coverage-report
84+
path: coverage.xml
85+
86+
js-tests:
87+
name: Frontend unit tests (vitest)
88+
runs-on: ubuntu-latest
89+
steps:
90+
- uses: actions/checkout@v4
91+
92+
- uses: actions/setup-node@v4
93+
with:
94+
node-version: "20"
95+
cache: npm
96+
97+
- run: npm ci
98+
- run: npm test

.gitignore

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

api/search.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,33 @@
1010

1111
search_bp = Blueprint("search", __name__)
1212

13+
_DEFAULT_LIMIT = 50
14+
_MAX_LIMIT = 500
15+
16+
17+
def _parse_limit(raw: str | None) -> int:
18+
"""Parse positive integer limit query param; raise ValueError if invalid."""
19+
if raw is None or raw == "":
20+
return _DEFAULT_LIMIT
21+
try:
22+
n = int(raw)
23+
except ValueError:
24+
raise ValueError("limit must be a positive integer") from None
25+
if n < 1:
26+
raise ValueError("limit must be a positive integer")
27+
return min(n, _MAX_LIMIT)
28+
1329

1430
@search_bp.route("/api/search")
1531
def search():
1632
query = request.args.get("q", "").strip().lower()
1733
if not query:
1834
return jsonify([])
1935

20-
max_results = int(request.args.get("limit", 50))
36+
try:
37+
max_results = _parse_limit(request.args.get("limit"))
38+
except ValueError:
39+
return jsonify({"error": "Invalid limit: must be a positive integer"}), 400
2140
base = current_app.config.get("CLAUDE_PROJECTS_DIR") or get_claude_projects_dir()
2241
projects = list_projects(base)
2342

0 commit comments

Comments
 (0)