Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#!/bin/bash

set -e

repo_root=$(git rev-parse --show-toplevel)
cd "$repo_root"

if [ ! -f requirements.txt ]; then
echo "Error: requirements.txt is missing from the repository root."
exit 1
fi

echo "Checking code style with Black..."
make format-check

if [ -n "${TEST_FILES:-}" ]; then
echo "Running selected tests from TEST_FILES..."
make test-fast TEST_FILES="$TEST_FILES"
elif [ "${TEST_SCOPE:-fast}" = "full" ]; then
echo "Running the full test and coverage gate..."
make test-full
else
staged_tests=()
unknown_python_change=0

while IFS= read -r path; do
case "$path" in
tests/test_*.py)
staged_tests+=("$path")
;;
src/compression/*)
staged_tests+=("tests/test_compression.py")
;;
src/data_structures/*)
staged_tests+=("tests/test_data_structures.py")
staged_tests+=("tests/test_service_tools.py" "tests/test_service_mcp_server.py" "tests/test_service_http_app.py")
;;
src/dynamic_programming/*)
staged_tests+=("tests/test_dynamic_programming.py")
staged_tests+=("tests/test_service_tools.py" "tests/test_service_mcp_server.py" "tests/test_service_http_app.py")
;;
src/graphs/*)
staged_tests+=("tests/test_graphs.py")
staged_tests+=("tests/test_service_tools.py" "tests/test_service_mcp_server.py" "tests/test_service_http_app.py")
;;
src/machine_learning/*)
staged_tests+=("tests/test_machine_learning.py")
staged_tests+=("tests/test_service_tools.py" "tests/test_service_mcp_server.py" "tests/test_service_http_app.py")
;;
src/numeric/*)
staged_tests+=("tests/test_numeric.py")
staged_tests+=("tests/test_service_tools.py" "tests/test_service_mcp_server.py" "tests/test_service_http_app.py")
;;
src/searching/*)
staged_tests+=("tests/test_searching.py")
staged_tests+=("tests/test_service_tools.py" "tests/test_service_mcp_server.py" "tests/test_service_http_app.py")
;;
src/sorting/*)
staged_tests+=("tests/test_sorting.py")
staged_tests+=("tests/test_service_tools.py" "tests/test_service_mcp_server.py" "tests/test_service_http_app.py")
;;
src/string_matching/*)
staged_tests+=("tests/test_string_matching.py")
staged_tests+=("tests/test_service_tools.py" "tests/test_service_mcp_server.py" "tests/test_service_http_app.py")
;;
service/*)
staged_tests+=("tests/test_service_tools.py" "tests/test_service_mcp_server.py" "tests/test_service_http_app.py")
;;
*.py)
unknown_python_change=1
;;
esac
done < <(git diff --cached --name-only --diff-filter=ACMR)

if [ "$unknown_python_change" -eq 1 ]; then
echo "Running the full test suite for an unmapped Python change..."
make test-full
elif [ "${#staged_tests[@]}" -eq 0 ]; then
echo "No staged Python changes; skipping tests."
else
unique_tests=($(printf '%s\n' "${staged_tests[@]}" | sort -u))
echo "Running staged-area tests: ${unique_tests[*]}"
make test-fast TEST_FILES="${unique_tests[*]}"
fi
fi

echo "Pre-commit checks passed."
11 changes: 3 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,9 @@ jobs:

- name: Execute Automated Test Suite with Coverage Tracking
run: |
# Generates terminal outputs, an interactive HTML site, and an XML map
# Enforces a strict minimum test coverage baseline barrier of 100%
pytest tests/ \
--cov=src \
--cov-fail-under=100 \
--cov-report=term-missing \
--cov-report=html:htmlcov \
--cov-report=xml:coverage.xml
# Enforces the shared 100% coverage gate used by full local verification.
make test-full \
PYTEST="pytest --cov-report=html:htmlcov --cov-report=xml:coverage.xml"

# Packages the generated interactive HTML site as a downloadable dashboard
- name: Upload Core Coverage HTML Dashboard
Expand Down
13 changes: 13 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,18 @@ venv/
.venv/
.pytest_cache/
.coverage
coverage.xml
htmlcov/
\.env
\.env.*
!\.env.example
*.pem
*.key
*.p12
*.pfx
*.secret
*.secrets
*.log
\.DS_Store
*.txt
!requirements.txt
15 changes: 15 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
PYTEST ?= pytest
BLACK ?= black

.PHONY: format-check test-fast test-full verify

format-check:
$(BLACK) --check src/ tests/ service/

test-fast:
$(PYTEST) $(TEST_FILES)

test-full:
$(PYTEST) tests/ --cov=src --cov=service --cov-fail-under=100 --cov-report=term-missing

verify: format-check test-full
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ To run syntax and style validation checks using `flake8` or `black`:
black --check src/ tests/
```

For the full test and coverage gate, run `make verify`. Local commits use the tracked `.githooks/pre-commit` hook for fast staged-area tests by default; use `TEST_SCOPE=full git commit` to run the complete 100% coverage gate before committing.

## Running the Service Layer

Every algorithm is also exposed via a stateless MCP server and a REST API, both backed by the same `service/tools.py` wrapper functions.
Expand Down
28 changes: 22 additions & 6 deletions docs/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ source .venv/bin/activate
pip install -r requirements.txt
```

Do not commit credentials, local environment files, private keys, coverage reports, or logs. The repository ignores common forms of these files; review `git status` and `git diff --cached` before pushing, and use environment variables for any local secrets.

`.venv/` is self-ignored (it ships its own `.gitignore`), so it never needs to be added to the repo's ignore rules. Use `.venv/bin/pytest`, `.venv/bin/black`, etc. if the venv isn't activated in your shell.

> **Two Python environments exist.** The local pre-commit hook (see Section 4) runs `pytest`/`black` from a separate, globally-installed interpreter — not `.venv`. If you add a new dependency (e.g. to `service/`), install it in **both** places, or the hook will fail with `ModuleNotFoundError` at commit time:
Expand Down Expand Up @@ -43,14 +45,27 @@ Each new algorithm addition should include, as applicable:

## 4. Formatting & Local Verification

Run these before committing — a pre-commit hook enforces them anyway, but running locally first avoids failed commits:
The tracked `.githooks/pre-commit` hook runs Black plus tests related to staged Python files by default. Install it for a clone with:

```bash
git config core.hooksPath .githooks
```

Use the fast default for normal commits. To run specific tests:

```bash
TEST_FILES="tests/test_graphs.py" git commit
```

To run the complete repository gate locally:

```bash
.venv/bin/black src/ tests/ service/
.venv/bin/pytest tests/ --cov=src --cov=service --cov-report=term-missing
TEST_SCOPE=full git commit
# or, without committing:
make verify
```

Confirm the coverage report shows 100% for all modified/added files.
The full gate enforces 100% coverage across `src` and `service`; selective tests are intended for quick local feedback, while CI always runs the full gate.

## 5. Committing

Expand Down Expand Up @@ -107,8 +122,9 @@ git fetch origin --prune # clears stale remote-tracking
|---|---|
| Sync main | `git checkout main && git pull origin main` |
| New branch | `git checkout -b feature/<name>` |
| Format | `.venv/bin/black src/ tests/ service/` |
| Test + coverage | `.venv/bin/pytest tests/ --cov=src --cov=service --cov-report=term-missing` |
| Install hooks | `git config core.hooksPath .githooks` |
| Fast selected tests | `TEST_FILES="tests/test_graphs.py" git commit` |
| Full test + coverage | `TEST_SCOPE=full git commit` or `make verify` |
| Push | `git push -u origin <branch>` |
| Open PR | `gh pr create --title "..." --body "..." --base main --head <branch>` |
| Merge PR | `gh pr merge --squash --delete-branch` |
Expand Down
Loading