Skip to content

Commit 2ab2659

Browse files
jmchiltonclaude
andcommitted
Add IWC integration tests for lint (structural, pydantic, best practices)
Runs lint_ga, lint_pydantic_validation, and lint_best_practices_ga on all IWC .ga workflows when GXFORMAT2_TEST_IWC_DIRECTORY is set. Skips when unset. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 541c681 commit 2ab2659

File tree

4 files changed

+81
-37
lines changed

4 files changed

+81
-37
lines changed

tests/_helpers.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import copy
2+
import glob
23
import os
34

45
from gxformat2.converter import python_to_workflow, yaml_to_workflow
@@ -9,6 +10,31 @@
910
TEST_PATH = os.path.abspath(os.path.dirname(__file__))
1011
TEST_INTEROP_EXAMPLES = os.environ.get("GXFORMAT2_INTEROP_EXAMPLES", os.path.join(TEST_PATH, "examples"))
1112

13+
IWC_DIR = os.environ.get("GXFORMAT2_TEST_IWC_DIRECTORY")
14+
15+
# TODO: remove after https://github.com/galaxyproject/iwc/pull/1167 is merged
16+
IWC_SKIP = {
17+
"hic-fastq-to-cool-hicup-cooler.ga",
18+
"chic-fastq-to-cool-hicup-cooler.ga",
19+
}
20+
21+
22+
def find_iwc_ga_files(skip=None):
23+
"""Find .ga workflow files in the IWC checkout, optionally skipping some by basename."""
24+
if not IWC_DIR:
25+
return []
26+
skip = skip or IWC_SKIP
27+
return sorted(
28+
p
29+
for p in glob.glob(os.path.join(IWC_DIR, "workflows", "**", "*.ga"), recursive=True)
30+
if os.path.basename(p) not in skip
31+
)
32+
33+
34+
def iwc_fixture_ids(paths):
35+
"""Generate pytest fixture IDs relative to IWC_DIR."""
36+
return [os.path.relpath(p, IWC_DIR) if IWC_DIR else p for p in paths]
37+
1238

1339
def to_native(has_yaml, **kwds):
1440
if isinstance(has_yaml, dict):

tests/test_lint.py

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,23 @@
11
import copy
2+
import json
23
import os
34

4-
from gxformat2.lint import main
5+
import pytest
6+
7+
from gxformat2.lint import (
8+
lint_best_practices_ga,
9+
lint_ga,
10+
lint_pydantic_validation,
11+
main,
12+
)
13+
from gxformat2.linting import LintContext
514
from gxformat2.yaml import ordered_dump, ordered_load
615
from ._helpers import (
716
assert_valid_native,
817
copy_without_workflow_output_labels,
18+
find_iwc_ga_files,
19+
IWC_DIR,
20+
iwc_fixture_ids,
921
round_trip,
1022
TEST_INTEROP_EXAMPLES,
1123
TEST_PATH,
@@ -306,3 +318,39 @@ def _dump_with_exit_code(as_dict, exit_code, description):
306318
with open(os.path.join(TEST_LINT_EXAMPLES, "%d_%s.yml" % (exit_code, description)), "w") as fd:
307319
ordered_dump(as_dict, fd)
308320
fd.flush()
321+
322+
323+
# --- IWC integration tests ---
324+
325+
GA_FILES = find_iwc_ga_files()
326+
327+
328+
@pytest.mark.skipif(IWC_DIR is None, reason="GXFORMAT2_TEST_IWC_DIRECTORY not set")
329+
class TestIWCLint:
330+
331+
@pytest.fixture(
332+
params=GA_FILES,
333+
ids=iwc_fixture_ids(GA_FILES),
334+
)
335+
def ga_path_and_dict(self, request):
336+
with open(request.param) as f:
337+
return request.param, json.load(f)
338+
339+
def test_structural_lint(self, ga_path_and_dict):
340+
path, workflow_dict = ga_path_and_dict
341+
ctx = LintContext()
342+
lint_ga(ctx, workflow_dict, path=path)
343+
assert not ctx.error_messages, f"Structural lint errors in {os.path.basename(path)}: {ctx.error_messages}"
344+
345+
def test_pydantic_validation(self, ga_path_and_dict):
346+
_, workflow_dict = ga_path_and_dict
347+
ctx = LintContext()
348+
lint_pydantic_validation(ctx, workflow_dict, format2=False)
349+
assert not ctx.error_messages, f"Pydantic validation errors: {ctx.error_messages}"
350+
351+
def test_best_practices(self, ga_path_and_dict):
352+
_, workflow_dict = ga_path_and_dict
353+
ctx = LintContext()
354+
lint_best_practices_ga(ctx, workflow_dict)
355+
# Best practices produce warnings, not errors — just ensure no crash.
356+
# Warnings are expected for many IWC workflows.

tests/test_native_schema.py

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@
77
found in that IWC checkout.
88
"""
99

10-
import glob
1110
import json
1211
import os
1312
import tempfile
1413

1514
import pytest
1615

16+
from ._helpers import find_iwc_ga_files, IWC_DIR, iwc_fixture_ids
1717
from gxformat2.schema.native_v0_1 import load_document
1818

1919

@@ -213,24 +213,15 @@ def test_with_creator_mixed(self):
213213

214214
# --- IWC integration tests ---
215215

216-
IWC_DIR = os.environ.get("GXFORMAT2_TEST_IWC_DIRECTORY")
217-
218-
219-
def _find_ga_files():
220-
if not IWC_DIR:
221-
return []
222-
return sorted(glob.glob(os.path.join(IWC_DIR, "workflows", "**", "*.ga"), recursive=True))
223-
224-
225-
GA_FILES = _find_ga_files()
216+
GA_FILES = find_iwc_ga_files()
226217

227218

228219
@pytest.mark.skipif(IWC_DIR is None, reason="GXFORMAT2_TEST_IWC_DIRECTORY not set")
229220
class TestIWCWorkflows:
230221

231222
@pytest.fixture(
232223
params=GA_FILES,
233-
ids=[os.path.relpath(p, IWC_DIR) if IWC_DIR else p for p in GA_FILES],
224+
ids=iwc_fixture_ids(GA_FILES),
234225
)
235226
def ga_workflow(self, request):
236227
with open(request.param) as f:

tests/test_pydantic_schema.py

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,11 @@
44
can load and validate both Format2 and native Galaxy workflow documents.
55
"""
66

7-
import glob
87
import json
9-
import os
108

119
import pytest
1210

11+
from ._helpers import find_iwc_ga_files, IWC_DIR, iwc_fixture_ids
1312
from gxformat2.export import from_galaxy_native
1413
from gxformat2.schema.gxformat2 import (
1514
GalaxyWorkflow,
@@ -290,35 +289,15 @@ def test_extra_fields_allowed(self):
290289

291290
# --- IWC integration tests ---
292291

293-
IWC_DIR = os.environ.get("GXFORMAT2_TEST_IWC_DIRECTORY")
294-
295-
296-
# TODO: remove after https://github.com/galaxyproject/iwc/pull/1167 is merged
297-
_IWC_PYDANTIC_SKIP = {
298-
"hic-fastq-to-cool-hicup-cooler.ga",
299-
"chic-fastq-to-cool-hicup-cooler.ga",
300-
}
301-
302-
303-
def _find_ga_files():
304-
if not IWC_DIR:
305-
return []
306-
return sorted(
307-
p
308-
for p in glob.glob(os.path.join(IWC_DIR, "workflows", "**", "*.ga"), recursive=True)
309-
if os.path.basename(p) not in _IWC_PYDANTIC_SKIP
310-
)
311-
312-
313-
GA_FILES = _find_ga_files()
292+
GA_FILES = find_iwc_ga_files()
314293

315294

316295
@pytest.mark.skipif(IWC_DIR is None, reason="GXFORMAT2_TEST_IWC_DIRECTORY not set")
317296
class TestIWCPydantic:
318297

319298
@pytest.fixture(
320299
params=GA_FILES,
321-
ids=[os.path.relpath(p, IWC_DIR) if IWC_DIR else p for p in GA_FILES],
300+
ids=iwc_fixture_ids(GA_FILES),
322301
)
323302
def ga_workflow(self, request):
324303
with open(request.param) as f:

0 commit comments

Comments
 (0)