|
| 1 | +# Contributing to linkml-project-copier |
| 2 | + |
| 3 | +Thank you for considering a contribution! |
| 4 | + |
| 5 | +## Prerequisites |
| 6 | + |
| 7 | +- **Python >= 3.10** |
| 8 | +- **uv** -- for dependency management and running tests |
| 9 | +- **just** -- only needed if you want to run integration tests locally |
| 10 | + (`uv tool install rust-just`) |
| 11 | + |
| 12 | +## Getting started |
| 13 | + |
| 14 | +```shell |
| 15 | +git clone https://github.com/linkml/linkml-project-copier.git |
| 16 | +cd linkml-project-copier |
| 17 | +uv sync --group test |
| 18 | +``` |
| 19 | + |
| 20 | +## Running the tests |
| 21 | + |
| 22 | +The test suite has two tiers: |
| 23 | + |
| 24 | +```shell |
| 25 | +# Structural tests only (fast, ~25 seconds, no just/linkml needed) |
| 26 | +uv run pytest -m "not integration" -v |
| 27 | + |
| 28 | +# Integration tests only (slow, minutes, needs just + network) |
| 29 | +uv run pytest -m integration -v |
| 30 | + |
| 31 | +# Everything |
| 32 | +uv run pytest -v |
| 33 | +``` |
| 34 | + |
| 35 | +### Important: commit before testing |
| 36 | + |
| 37 | +The tests use copier's Python API with `vcs_ref="HEAD"`, which means copier |
| 38 | +generates projects from the **last commit** on the current branch. If you |
| 39 | +modify template files without committing, the tests will run against the |
| 40 | +old commit and your changes won't be covered. |
| 41 | + |
| 42 | +## Test architecture |
| 43 | + |
| 44 | +### Two tiers |
| 45 | + |
| 46 | +**Structural tests** (`test_generation.py`, `test_options.py`, |
| 47 | +`test_licenses.py`) generate projects via copier's Python API and inspect |
| 48 | +the output -- file existence, content, template variable substitution. They |
| 49 | +are fast (seconds) and need nothing beyond the test dependencies. These |
| 50 | +tests must never modify the generated project. |
| 51 | + |
| 52 | +**Integration tests** (`test_integration.py`) generate a project, then run |
| 53 | +`just install`, `just test`, `just lint`, and `just gen-doc` via subprocess. |
| 54 | +They exercise the full toolchain (uv, linkml, just) and take minutes. |
| 55 | + |
| 56 | +### Fixture design |
| 57 | + |
| 58 | +Generating a project with copier takes 1-3 seconds. With 80+ structural |
| 59 | +tests, per-test generation would be very slow. To avoid this: |
| 60 | + |
| 61 | +- **Session-scoped fixtures** (in `conftest.py`) generate each project |
| 62 | + variant once and share it across all structural tests. The trade-off: |
| 63 | + structural tests must treat the generated project as **read-only**. |
| 64 | +- **Module-scoped fixture** for integration tests generates a fresh project |
| 65 | + because `just` commands mutate the project directory (installing packages, |
| 66 | + generating files). |
| 67 | + |
| 68 | +Available session fixtures: `default_project`, `no_example_project`, |
| 69 | +`no_pypi_project`, `no_docs_preview_project`, and `license_project` |
| 70 | +(parametrized across all six license types). |
| 71 | + |
| 72 | +### Shared helpers (`tests/helpers.py`) |
| 73 | + |
| 74 | +| Helper | Purpose | |
| 75 | +|--------|---------| |
| 76 | +| `generate_project(dest, data_overrides)` | Call copier's `run_copy()` with sensible defaults | |
| 77 | +| `git_init(project_dir)` | Init git + initial commit (needed for dynamic versioning) | |
| 78 | +| `run_just(project_dir, *args)` | Run a just command via subprocess with timeout | |
| 79 | +| `DEFAULT_DATA` | Dict of template variable defaults used by all tests | |
| 80 | +| `ALL_LICENSES` | List of all six supported license identifiers | |
| 81 | + |
| 82 | +### Adding a new structural test |
| 83 | + |
| 84 | +1. Pick the right fixture. If you need the default project, use |
| 85 | + `default_project`. If you need a specific option combination that |
| 86 | + doesn't exist yet, add a new session-scoped fixture in `conftest.py`. |
| 87 | +2. Put the test in the appropriate module (`test_generation.py` for general |
| 88 | + structure, `test_options.py` for boolean flags, `test_licenses.py` for |
| 89 | + license variants). |
| 90 | +3. Never modify files inside the generated project directory -- session |
| 91 | + fixtures are shared. |
| 92 | + |
| 93 | +### Adding a new integration test |
| 94 | + |
| 95 | +Add the test to `test_integration.py`. It receives the `integration_project` |
| 96 | +fixture which already has `just install` run in it, so dependencies are |
| 97 | +available. Mark the test (or the whole module) with `@pytest.mark.integration`. |
| 98 | + |
| 99 | +## CI |
| 100 | + |
| 101 | +The GitHub Actions workflow `test-template.yml` runs two jobs: |
| 102 | + |
| 103 | +- **structural** -- fast, Python version matrix, Ubuntu only |
| 104 | +- **integration** -- slow, OS matrix (Ubuntu + Windows), Python version matrix |
| 105 | + |
| 106 | +Structural tests run on every push and PR. Integration tests also run on |
| 107 | +every push and PR but take longer, so they use a smaller matrix focused on |
| 108 | +OS coverage. |
0 commit comments