Skip to content

Commit e8bcc7b

Browse files
edmundmillerclaude
andcommitted
feat: Add lint check for version snapshot content validation
Add new lint check `test_snap_version_content` to ensure version information in test snapshots contains actual content instead of MD5/SHA hash values. This addresses the issue where version snapshots were storing hash values like "versions.yml:md5,949da9c6297b613b50e24c421576f3f1" instead of actual version content like {"ALE": {"ale": "20180904"}}. Changes: - Add version content validation in module_tests.py with regex patterns - Add comprehensive tests for both invalid (hash) and valid (content) cases - Add pytest issue marker support for linking tests to GitHub issues - Update pyproject.toml with new pytest marker configuration Fixes: nf-core/modules#6505 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent a512b07 commit e8bcc7b

File tree

3 files changed

+111
-5
lines changed

3 files changed

+111
-5
lines changed

nf_core/modules/lint/module_tests.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,36 @@ def module_tests(_, module: NFCoreComponent, allow_missing: bool = False):
170170
snap_file,
171171
)
172172
)
173+
# Check if version content is actual content vs MD5 hash
174+
# Related to: https://github.com/nf-core/modules/issues/6505
175+
# Ensures version snapshots contain actual content instead of hash values
176+
version_content_valid = True
177+
version_hash_patterns = [
178+
r"versions\.yml:md5,[\da-f]+", # MD5 hash pattern
179+
r"versions\.yml:sha[\d]*,[\da-f]+", # SHA hash pattern
180+
]
181+
182+
for pattern in version_hash_patterns:
183+
if re.search(pattern, str(snap_content[test_name])):
184+
version_content_valid = False
185+
break
186+
187+
if version_content_valid:
188+
module.passed.append(
189+
(
190+
"test_snap_version_content",
191+
"version information contains actual content instead of hash",
192+
snap_file,
193+
)
194+
)
195+
else:
196+
module.failed.append(
197+
(
198+
"test_snap_version_content",
199+
"version information should contain actual content, not MD5/SHA hash",
200+
snap_file,
201+
)
202+
)
173203
else:
174204
module.failed.append(
175205
(

pyproject.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ build-backend = "setuptools.build_meta"
33
requires = ["setuptools>=40.6.0", "wheel"]
44

55
[tool.pytest.ini_options]
6-
markers = ["datafiles: load datafiles"]
6+
markers = [
7+
"datafiles: load datafiles",
8+
"issue: mark test with related issue URL"
9+
]
710
testpaths = ["tests"]
811
python_files = ["test_*.py"]
912
norecursedirs = [

tests/modules/lint/test_module_tests.py

Lines changed: 77 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import json
22
from pathlib import Path
33

4+
import pytest
45
from git.repo import Repo
56

67
import nf_core.modules.lint
@@ -194,22 +195,94 @@ def test_modules_empty_file_in_stub_snapshot(self):
194195
assert len(module_lint.failed) == 0, f"Linting failed with {[x.__dict__ for x in module_lint.failed]}"
195196
assert len(module_lint.passed) > 0
196197
assert len(module_lint.warned) >= 0
197-
198+
198199
# Check for test_snap_md5sum in passed tests (handle both tuple and LintResult formats)
199200
found_test = False
200201
for x in module_lint.passed:
201202
test_name = None
202-
if hasattr(x, 'lint_test'):
203+
if hasattr(x, "lint_test"):
203204
test_name = x.lint_test
204205
elif isinstance(x, tuple) and len(x) > 0:
205206
test_name = x[0]
206-
207+
207208
if test_name == "test_snap_md5sum":
208209
found_test = True
209210
break
210-
211+
211212
assert found_test, "test_snap_md5sum not found in passed tests"
212213

213214
# reset the file
214215
with open(snap_file, "w") as fh:
215216
fh.write(content)
217+
218+
@pytest.mark.issue("https://github.com/nf-core/modules/issues/6505")
219+
def test_modules_version_snapshot_content_md5_hash(self):
220+
"""Test linting a nf-test module with version information as MD5 hash instead of actual content, which should fail.
221+
222+
Related to: https://github.com/nf-core/modules/issues/6505
223+
Fixed in: https://github.com/nf-core/modules/pull/7098
224+
"""
225+
snap_file = self.bpipe_test_module_path / "tests" / "main.nf.test.snap"
226+
snap = json.load(snap_file.open())
227+
content = snap_file.read_text()
228+
229+
# Add a version entry with MD5 hash format (the old way that should be flagged)
230+
snap["my test"]["content"][0]["versions"] = "versions.yml:md5,949da9c6297b613b50e24c421576f3f1"
231+
232+
with open(snap_file, "w") as fh:
233+
json.dump(snap, fh)
234+
235+
module_lint = nf_core.modules.lint.ModuleLint(directory=self.nfcore_modules)
236+
module_lint.lint(print_results=False, module="bpipe/test")
237+
238+
# Should fail because version is using MD5 hash instead of actual content
239+
# Filter for only our specific test
240+
version_content_failures = [x for x in module_lint.failed if x.lint_test == "test_snap_version_content"]
241+
assert len(version_content_failures) == 1, (
242+
f"Expected 1 test_snap_version_content failure, got {len(version_content_failures)}"
243+
)
244+
assert version_content_failures[0].lint_test == "test_snap_version_content"
245+
246+
# reset the file
247+
with open(snap_file, "w") as fh:
248+
fh.write(content)
249+
250+
@pytest.mark.issue("https://github.com/nf-core/modules/issues/6505")
251+
def test_modules_version_snapshot_content_valid(self):
252+
"""Test linting a nf-test module with version information as actual content, which should pass.
253+
254+
Related to: https://github.com/nf-core/modules/issues/6505
255+
Fixed in: https://github.com/nf-core/modules/pull/7098
256+
"""
257+
snap_file = self.bpipe_test_module_path / "tests" / "main.nf.test.snap"
258+
snap = json.load(snap_file.open())
259+
content = snap_file.read_text()
260+
261+
# Add a version entry with actual content (the new way that should pass)
262+
snap["my test"]["content"][0]["versions"] = {"ALE": {"ale": "20180904"}}
263+
264+
with open(snap_file, "w") as fh:
265+
json.dump(snap, fh)
266+
267+
module_lint = nf_core.modules.lint.ModuleLint(directory=self.nfcore_modules)
268+
module_lint.lint(print_results=False, module="bpipe/test")
269+
270+
# Should pass because version contains actual content
271+
# Filter for only our specific test
272+
version_content_failures = [x for x in module_lint.failed if x.lint_test == "test_snap_version_content"]
273+
assert len(version_content_failures) == 0, (
274+
f"Expected 0 test_snap_version_content failures, got {len(version_content_failures)}"
275+
)
276+
277+
# Check for test_snap_version_content in passed tests
278+
version_content_passed = [
279+
x
280+
for x in module_lint.passed
281+
if (hasattr(x, "lint_test") and x.lint_test == "test_snap_version_content")
282+
or (isinstance(x, tuple) and len(x) > 0 and x[0] == "test_snap_version_content")
283+
]
284+
assert len(version_content_passed) > 0, "test_snap_version_content not found in passed tests"
285+
286+
# reset the file
287+
with open(snap_file, "w") as fh:
288+
fh.write(content)

0 commit comments

Comments
 (0)