|
| 1 | +--- |
| 2 | +description: Testing and coverage when new code is added — pre-commit, unit tests, coverage (no E2E/notebooks) |
| 3 | +globs: "**/test_*.py,**/*_test.py,**/tests/**/*.py,src/**/*.py,.github/workflows/*.yml,.github/workflows/*.yaml" |
| 4 | +alwaysApply: true |
| 5 | +--- |
| 6 | + |
| 7 | +# Testing & coverage (new code) |
| 8 | + |
| 9 | +When **new code is added**, run this validation pipeline: **pre-commit**, **unit tests**, and **coverage**. Do **not** run E2E or notebook tests (they take too long). |
| 10 | + |
| 11 | +--- |
| 12 | + |
| 13 | +## Cursor: no write/CRUD git commands |
| 14 | + |
| 15 | +- **Cursor MUST NOT run any git commands that modify state** (e.g. no `git commit`, `git push`, `git add`, `git merge`, `git rebase`, `git reset --hard`, etc.). The user runs those themselves. |
| 16 | +- **Cursor MAY run read-only git commands** when useful (e.g. `git status`, `git log`, `git diff`, `git show`, `git checkout -- <path>` to restore a file, `git branch -a`, etc.). |
| 17 | +--- |
| 18 | + |
| 19 | +## Validation when adding new code |
| 20 | + |
| 21 | +- **When to run**: If you added or changed code in this session, run the validation pipeline **once, just before ending the conversation**. Do not run pre-commit, unit tests, or coverage after every small edit; run them only at the end so the user sees a single pass/fail before they leave. |
| 22 | + |
| 23 | +**Quick commands** (unit tests only, no E2E/notebooks; then check **patch** coverage only, not full codebase): |
| 24 | +```bash |
| 25 | +pre-commit run --show-diff-on-failure --color=always --all-files |
| 26 | +poetry install --with test |
| 27 | +coverage run --omit="src/**/test_*.py,src/codeflare_sdk/common/utils/unit_test_support.py,src/codeflare_sdk/vendored/**" -m pytest --ignore=tests/e2e --ignore=tests/e2e_v2 --ignore=tests/upgrade --ignore=demo-notebooks --ignore=ui-tests |
| 28 | +coverage report -m |
| 29 | +# Then, for source files changed in this session only: coverage report -m --include="path/to/changed1.py,path/to/changed2.py" (require ≥85% for that subset; ignore overall %) |
| 30 | +``` |
| 31 | + |
| 32 | +### 1. Pre-commit |
| 33 | +- Run before considering changes done: `pre-commit run --all-files` (or the full command above). |
| 34 | +- Do not skip or alter pre-commit hooks. |
| 35 | + |
| 36 | +### 2. Unit tests (no E2E, no notebooks) |
| 37 | +- Run **only unit tests**. Do **not** run E2E or notebook tests (they take too long). Exclude: `tests/e2e`, `tests/e2e_v2`, `tests/upgrade`, `demo-notebooks`, `ui-tests`. |
| 38 | +- Use the coverage + pytest command from **Quick commands** above. |
| 39 | + |
| 40 | +### 3. Coverage requirements (in this session) |
| 41 | +- **Check only patch coverage**: In Cursor, only validate coverage for **code added or changed in this chat** (the “patch”), not the full codebase. Full codebase coverage is enforced in GitHub CI and will already be ≥90% on main; local runs often show low overall % (e.g. below 30%) due to vendored code, kuberay client, and other excluded paths—**do not fail the session on that**. |
| 42 | +- **Patch target**: For source files you added or modified in this session, require **≥85%** coverage. After running the coverage commands below, run `coverage report -m --include="path/to/changed1.py,path/to/changed2.py,..."` with the actual paths you changed and ensure that subset is ≥85%. If you only changed tests or config, there is no patch coverage to check. |
| 43 | +- **CI / codecov**: GitHub runs full pytest and enforces ≥90% project coverage; codecov.yml uses patch 85%, threshold 2.5%. Ignore: `**/*.ipynb`, `demo-notebooks/**`, `**/__init__.py`. |
| 44 | + |
| 45 | +--- |
| 46 | + |
| 47 | +## Unit test stack (do not replace) |
| 48 | +- **Runner**: pytest 9.x. **Extras**: pytest-mock, pytest-timeout (default timeout 900s in pyproject.toml). **Coverage**: coverage 7.x. |
| 49 | + |
| 50 | +### Where unit tests live |
| 51 | +- Tests next to code: `src/codeflare_sdk/**/test_*.py`. Ignore: `src/codeflare_sdk/vendored/**`, `unit_test_support.py`. |
| 52 | +- **conftest.py**: The global `src/codeflare_sdk/conftest.py` auto-mocks K8s API clients; tests inherit these mocks. Override with `mocker` or `monkeypatch` when testing specific K8s/config behavior. Never make real Kubernetes API calls in unit tests. |
| 53 | + |
| 54 | +### Pytest markers (use when relevant) |
| 55 | +- `smoke` — quick validation. `tier1` — standard suite. |
| 56 | +- `kind`, `openshift`, `nvidia_gpu` — environment-specific (E2E only; do not run in the “new code” flow). |
| 57 | + |
| 58 | +### Writing tests |
| 59 | +- Use **mocker** (pytest-mock) for K8s/API calls; see `test_kueue.py` and `test_auth*.py` for patterns. |
| 60 | +- **NEVER** hardcode raw Kubernetes JSON payloads in test files. You **MUST** use or extend the helper functions in `src/codeflare_sdk/common/utils/unit_test_support.py` (e.g. `get_ray_obj_with_status`, `get_obj_none`, `get_local_queue`, `create_cluster_config`, `apply_template`). |
| 61 | +- New code in `src/codeflare_sdk` should have corresponding tests so patch coverage (for that new code) stays ≥85%; full project coverage is enforced in CI. |
| 62 | +- **Edge-case tests for API/CR parsing**: When adding code that parses Kubernetes Custom Resource or list/get API response dicts, add at least one test that uses **malformed or partial payloads**: empty `items`, missing `spec` or `status`, empty or missing nested lists (e.g. `workerGroupSpecs`). Assert safe defaults (e.g. 0 for counts, UNKNOWN or equivalent for status). Test error-handling by mocking the API to raise (e.g. `ApiException`); assert the handler is invoked (e.g. mock `_kube_api_error_handling` and `assert_called_once()`) rather than asserting exact stdout text. |
| 63 | + |
| 64 | +--- |
| 65 | + |
| 66 | +## CI workflows (reference only; E2E/notebooks not in “new code” flow) |
| 67 | + |
| 68 | +### Versions (CI env) |
| 69 | +- **KUEUE_VERSION**: v0.13.4. **KUBERAY_VERSION**: v1.4.2 (opendatahub-io/kuberay fork, RHOAI features). **Python**: 3.12. **Common repo**: project-codeflare/codeflare-common @ main (KinD/GPU setup). |
| 70 | + |
| 71 | +### Pre-commit (every PR / workflow_dispatch) |
| 72 | +- **Run before pushing**: `pre-commit run --all-files`. Image: `quay.io/project-codeflare/codeflare-sdk-precommit:v0.0.1`. Do not skip or alter pre-commit hooks. |
| 73 | + |
| 74 | +### Unit tests (every PR) |
| 75 | +- `poetry install --with test` then pytest with coverage ≥90%. No paths-ignore for this workflow. |
| 76 | + |
| 77 | +### RayJob E2E (PR to main, release-*, ray-jobs-feature) |
| 78 | +- **Paths-ignore**: docs/**, **.adoc, **.md, LICENSE. **Runner**: gpu-t4-4-core. KinD + NVIDIA GPU operator + Kueue + KubeRay. |
| 79 | +- **Command**: `poetry run pytest -v -s ./tests/e2e/rayjob/` |
| 80 | +- **RBAC**: sdk-user with limited permissions (rayclusters, rayjobs, localqueues, clusterqueues, resourceflavors, pods, services, secrets, workloads, etc.). Do not assume cluster-admin. |
| 81 | + |
| 82 | +### General E2E (same branches as RayJob E2E) |
| 83 | +- **Command**: `poetry run pytest -v -s ./tests/e2e/ -m 'kind and nvidia_gpu'` |
| 84 | +- E2E tests in `tests/e2e/` must use `@pytest.mark.kind` and `@pytest.mark.nvidia_gpu` to run in this workflow. Place RayJob e2e in `tests/e2e/rayjob/`; other e2e in `tests/e2e/`. Install for e2e: `poetry install --with test,docs`. |
| 85 | + |
| 86 | +### Guided notebooks (label: test-guided-notebooks) |
| 87 | +- KinD + Kueue + KubeRay; no GPU. Notebooks: 0_basic_ray, 4_rayjob_existing_cluster, 5_submit_rayjob_cr. Run with papermill (see Demo notebooks below). |
| 88 | + |
| 89 | +### UI notebooks (labels: test-guided-notebooks or test-ui-notebooks) |
| 90 | +- Job: verify-3_widget_example. Playwright (chromium) in `ui-tests/`; notebook `demo-notebooks/guided-demos/3_widget_example.ipynb`. |
| 91 | + |
| 92 | +### Additional notebooks (label: test-additional-notebooks) |
| 93 | +- local_interactive.ipynb and ray_job_client.ipynb are **skipped** in CI (mTLS/OpenShift required; not available in KinD). |
| 94 | + |
| 95 | +--- |
| 96 | + |
| 97 | +## Demo notebooks & CI |
| 98 | + |
| 99 | +### Notebooks executed in CI |
| 100 | + |
| 101 | +| Notebook | Workflow | Notes | |
| 102 | +|----------|----------|--------| |
| 103 | +| 0_basic_ray.ipynb | Guided | KinD: namespace='default', dashboard_check=False, remove auth cells | |
| 104 | +| 4_rayjob_existing_cluster.ipynb | Guided | KinD: namespace='default', GPU 0, remove oc login cell | |
| 105 | +| 5_submit_rayjob_cr.ipynb | Guided | KinD: namespace='default', remove oc login cell | |
| 106 | +| 3_widget_example.ipynb | UI | Playwright in ui-tests/; namespace='default', view_clusters('default'), remove auth cells | |
| 107 | + |
| 108 | +**Skipped in CI** (require mTLS/OpenShift): local_interactive.ipynb, ray_job_client.ipynb. |
| 109 | + |
| 110 | +### KinD-specific adaptations (CI applies these) |
| 111 | +- **Auth**: Remove cells that do auth/login (e.g. "Create authentication object for user permissions", `auth.logout()`, `oc login`) — KinD doesn't support token auth the same way. |
| 112 | +- **Namespace**: Use `namespace='default'` where the SDK needs it. Replace `namespace="your-namespace"` with `namespace="default"` in notebooks. |
| 113 | +- **Dashboard**: Use `cluster.wait_ready(dashboard_check=False)` in KinD (no HTTPRoute/Route). |
| 114 | +- **GPU**: In KinD jobs without GPU, set GPU requests to 0 (e.g. `head_extended_resource_requests={'nvidia.com/gpu':0}`). |
| 115 | +- **Widget**: For 3_widget_example, call `view_clusters('default')` with explicit namespace. |
| 116 | + |
| 117 | +When editing guided demos, keep them runnable on both real OpenShift and KinD; CI runs on KinD with the above edits applied in the workflow. |
| 118 | + |
| 119 | +### How notebooks are run |
| 120 | +- **Guided**: `poetry run papermill <notebook>.ipynb <notebook>_out.ipynb --log-output --execution-timeout 600` from `demo-notebooks/guided-demos`. Install: `poetry install --with test,docs`; for papermill also `pip install papermill ipython ipykernel`. |
| 121 | +- **UI**: From `ui-tests/`, `poetry run yarn test` (Playwright). Dependencies: `yarn install`, `yarn playwright install chromium`. |
| 122 | + |
| 123 | +### Adding a new notebook that should run in CI |
| 124 | +- **Guided**: Add a job in the Guided notebooks workflow (similar to verify-0_basic_ray), apply the same KinD adaptations in the workflow steps. |
| 125 | +- **UI**: Add to ui-tests and ensure 3_widget_example pattern (namespace, auth removal) if it uses cluster/widget APIs. |
| 126 | +- Do not rely on mTLS or OpenShift-only features if the notebook should run in current KinD CI. |
0 commit comments