From 355355a80a59eceeb0db8f9a4368f900c725b4d8 Mon Sep 17 00:00:00 2001 From: Hongming Wang Date: Sun, 26 Apr 2026 06:21:22 -0700 Subject: [PATCH 1/2] test(workspace): centralize pytest-cov config + 92% floor (closes #1817) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Python workspace already runs pytest-cov in CI but with no threshold and inline-flagged config. CI run 24956647701 (2026-04-26 staging) reports 97% coverage on the package — well above the issue's 75% target. The actionable gap is locking in a floor so a regression can't sneak past, and centralizing config so local `pytest` matches CI. Changes: - workspace/pytest.ini — coverage flags moved into addopts (-q, --cov=., --cov-report=term-missing, --cov-fail-under=92). 92% = current 97% measurement minus the 5pp safety margin the issue's Step 3 prescribes. - workspace/.coveragerc (new) — [run] omit list and [report] skip_covered. coverage.py doesn't read pytest.ini sections, so the omit config has to live here. - .github/workflows/ci.yml — removed the inline --cov flags from the Python Lint & Test step; now reads from pytest.ini. Workflow stays the same single-command shape, just simpler. Result: any PR that drops coverage below 92% fails CI loudly. Floor ratchets up by replacing 92 with current measurement on a future test-writing pass — same shape as Go coverage gates landed elsewhere. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/ci.yml | 4 +++- workspace/.coveragerc | 13 +++++++++++++ workspace/pytest.ini | 15 ++++++++++++++- 3 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 workspace/.coveragerc diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2ee5fe5b..a9902658 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -283,7 +283,9 @@ jobs: cache: pip cache-dependency-path: workspace/requirements.txt - run: pip install -r requirements.txt pytest pytest-asyncio pytest-cov - - run: python -m pytest --tb=short -q --cov=. --cov-report=term-missing + # Coverage flags + fail-under floor moved into workspace/pytest.ini + # (issue #1817) so local `pytest` and CI use identical config. + - run: python -m pytest --tb=short # SDK + plugin validation moved to standalone repo: # github.com/Molecule-AI/molecule-sdk-python diff --git a/workspace/.coveragerc b/workspace/.coveragerc new file mode 100644 index 00000000..b14f2f88 --- /dev/null +++ b/workspace/.coveragerc @@ -0,0 +1,13 @@ +# coverage.py config — consumed by `pytest --cov` via the pytest-cov +# plugin. Lives here (not in pytest.ini) because coverage.py only reads +# .coveragerc / setup.cfg / tox.ini / pyproject.toml — the [coverage:*] +# sections in pytest.ini are silently ignored. See issue #1817. +[run] +omit = + */tests/* + */__init__.py + plugins_registry/* + +[report] +# Skip files at 100% in the term-missing output to keep CI logs readable. +skip_covered = True diff --git a/workspace/pytest.ini b/workspace/pytest.ini index 50e89f85..230622e5 100644 --- a/workspace/pytest.ini +++ b/workspace/pytest.ini @@ -3,4 +3,17 @@ testpaths = tests python_files = test_*.py python_functions = test_* asyncio_mode = auto -addopts = -q +# Coverage config moved here from .github/workflows/ci.yml so local +# `pytest` matches CI without operator-typed flags. cov-fail-under +# pins the floor at 92% — 5pp below the 97% measured on staging +# (run 24956647701, 2026-04-26). Floor exists so a regression that +# drops coverage doesn't sneak past CI; tightening above 92% should +# follow real measurement, not aspiration. See issue #1817. +addopts = + -q + --cov=. + --cov-report=term-missing + --cov-fail-under=92 +# Coverage omit / report config lives in workspace/.coveragerc — coverage.py +# only reads .coveragerc / setup.cfg / tox.ini / pyproject.toml, NOT +# pytest.ini, so [coverage:*] sections here would be silently ignored. From 5d294936b37df3643f1657e31064d7d390e09b24 Mon Sep 17 00:00:00 2001 From: Hongming Wang Date: Sun, 26 Apr 2026 06:24:36 -0700 Subject: [PATCH 2/2] =?UTF-8?q?fixup:=20lower=20coverage=20floor=2092?= =?UTF-8?q?=E2=86=9286=20to=20match=20post-omit=20measurement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 97% number from CI run 24956647701 was measured WITHOUT a .coveragerc omit list. Once this PR's prescribed omit set is in effect (`*/__init__.py`, `*/tests/*`, `plugins_registry/*` — files that don't carry behavior), the actual measurement of behavior-bearing code on the same staging snapshot is 91.11% (run 24957664272). 86% sits at the issue's prescribed `current − 5pp` margin and unblocks CI without lowering the bar in real terms. --- workspace/pytest.ini | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/workspace/pytest.ini b/workspace/pytest.ini index 230622e5..22fee4eb 100644 --- a/workspace/pytest.ini +++ b/workspace/pytest.ini @@ -5,15 +5,22 @@ python_functions = test_* asyncio_mode = auto # Coverage config moved here from .github/workflows/ci.yml so local # `pytest` matches CI without operator-typed flags. cov-fail-under -# pins the floor at 92% — 5pp below the 97% measured on staging -# (run 24956647701, 2026-04-26). Floor exists so a regression that -# drops coverage doesn't sneak past CI; tightening above 92% should +# pins the floor at 86% — 5pp below the 91.11% measured on staging +# (run 24957664272, 2026-04-26). Floor exists so a regression that +# drops coverage doesn't sneak past CI; tightening above 86% should # follow real measurement, not aspiration. See issue #1817. +# +# Why 86 not 92: the earlier 97% measurement was without the +# .coveragerc omit list. Once `*/__init__.py`, `*/tests/*`, and +# `plugins_registry/*` are excluded (the issue's prescribed omit +# set, more meaningful since those files don't carry behavior), +# the actual measurement of behavior-bearing code is 91.11% — and +# 86% sits at the issue's prescribed `current - 5pp` margin. addopts = -q --cov=. --cov-report=term-missing - --cov-fail-under=92 + --cov-fail-under=86 # Coverage omit / report config lives in workspace/.coveragerc — coverage.py # only reads .coveragerc / setup.cfg / tox.ini / pyproject.toml, NOT # pytest.ini, so [coverage:*] sections here would be silently ignored.