Skip to content

Commit 654af99

Browse files
committed
Test clone engine home precedence and compiler binding
1 parent aac6d13 commit 654af99

1 file changed

Lines changed: 106 additions & 0 deletions

File tree

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
"""Clone engine placement contracts; native startup is a separate offline audit.
2+
3+
No inference, live Job, image copies, or installed runtime is needed by these
4+
regressions. The compiler tests exercise its real selected-dependency producer.
5+
"""
6+
from pathlib import Path
7+
8+
import pytest
9+
10+
from test_ngs_native_components import native
11+
12+
13+
HOME_KEYS = ('BMS_WF_CLONE_NXF_HOME', 'NXF_HOME', 'BMS_NEXTFLOW_HOME', 'BMS_DATA')
14+
15+
16+
@pytest.fixture
17+
def homes(monkeypatch, tmp_path):
18+
monkeypatch.setenv('HOME', str(tmp_path / 'home'))
19+
for key in HOME_KEYS:
20+
monkeypatch.delenv(key, raising=False)
21+
return tmp_path
22+
23+
24+
@pytest.mark.parametrize('winner', range(6))
25+
def test_engine_home_precedence_and_dependency_binding(native, homes, monkeypatch, winner):
26+
from scripts.validate_wf_clone_runtime import resolve_nextflow_home
27+
28+
values = [homes / name for name in ('explicit', 'clone', 'launcher', 'managed', 'data')]
29+
for index, key in enumerate(HOME_KEYS, 1):
30+
if index >= winner:
31+
monkeypatch.setenv(key, str(values[index]))
32+
selected = str(values[0]) if winner == 0 else None
33+
expected = (values[winner] if winner < 4 else
34+
values[4] / 'nextflow' if winner == 4 else homes / 'home/.nextflow')
35+
assert resolve_nextflow_home(selected) == expected
36+
params = {'wf_clone_nxf_home': selected} if selected else {}
37+
_, paths = native.ngs_clone_runtime_dependencies(params)
38+
jars = [path for key, path in paths.items() if key.endswith('-one.jar')]
39+
assert jars == [expected / 'framework/25.10.0/nextflow-25.10.0-one.jar']
40+
41+
42+
def test_explicit_relocated_home_beats_inherited_engine(native, homes, monkeypatch):
43+
from scripts.validate_wf_clone_runtime import resolve_nextflow_home
44+
45+
monkeypatch.setenv('NXF_HOME', str(homes / 'outer'))
46+
monkeypatch.setenv('NXF_VER', '25.10.1')
47+
selected = homes / 'relocated runtime/nxf-home'
48+
assert resolve_nextflow_home(str(selected)) == selected
49+
_, paths = native.ngs_clone_runtime_dependencies({'wf_clone_nxf_home': str(selected)})
50+
jar = next(path for key, path in paths.items() if key.endswith('-one.jar'))
51+
assert jar == selected / 'framework/25.10.0/nextflow-25.10.0-one.jar'
52+
assert not selected.exists() # Resolution neither installs nor creates a cache.
53+
54+
55+
@pytest.mark.parametrize('assembly', [True, False])
56+
@pytest.mark.parametrize('explicit', [True, False])
57+
def test_normal_compiler_binds_only_selected_clone_engine(native, homes, monkeypatch, assembly, explicit):
58+
from services.nextflow import compile_nextflow_invocation
59+
60+
monkeypatch.setenv('NXF_HOME', str(homes / 'launcher'))
61+
monkeypatch.setenv('NXF_VER', '25.10.1')
62+
monkeypatch.setenv('NXF_DIST', str(homes / 'outer-only-framework'))
63+
params = {'ont_workflow_id': 'ont_construct_screening', 'ont_input_mode': 'fastq',
64+
'fastq_path': str(homes / 'reads.fastq'),
65+
'reference_fasta': str(homes / 'reference.fa'),
66+
'run_assembly': assembly, 'run_fastq_qc': True}
67+
if explicit:
68+
params['wf_clone_nxf_home'] = str(homes / 'relocated')
69+
invocation = compile_nextflow_invocation('nanopore', 'construct_screening', params,
70+
str(homes / 'results'), job_id='engine-selection')
71+
assert invocation.execution_plan.complete, invocation.execution_plan.blockers
72+
bound = invocation.native_parameters
73+
command = list(invocation.command)
74+
dependencies = invocation.execution_plan.dependencies
75+
jars = [row for row in dependencies if (row.relative_path or '').endswith('-one.jar')]
76+
if assembly:
77+
expected = str(homes / ('relocated' if explicit else 'launcher'))
78+
assert bound['wf_clone_nxf_home'] == expected
79+
assert command.count('--wf_clone_nxf_home') == 1
80+
assert command[command.index('--wf_clone_nxf_home') + 1] == expected
81+
assert len(jars) == 1
82+
_, paths = native.ngs_clone_runtime_dependencies(bound)
83+
assert paths[jars[0].logical_id] == Path(expected) / 'framework/25.10.0/nextflow-25.10.0-one.jar'
84+
else:
85+
assert not jars
86+
assert not any('wf-clone-validation' in row.logical_id for row in dependencies)
87+
if not explicit:
88+
assert 'wf_clone_nxf_home' not in bound
89+
assert '--wf_clone_nxf_home' not in command
90+
91+
92+
def test_clone_default_compiler_uses_managed_data_home(native, homes, monkeypatch):
93+
from services.nextflow import compile_nextflow_invocation
94+
95+
monkeypatch.setenv('BMS_DATA', str(homes / 'data'))
96+
params = {'ont_workflow_id': 'wf_clone_validation', 'ont_input_mode': 'fastq',
97+
'fastq_path': str(homes / 'reads.fastq'),
98+
'reference_fasta': str(homes / 'reference.fa')}
99+
invocation = compile_nextflow_invocation('nanopore', 'clone_validation', params,
100+
str(homes / 'results'), job_id='clone-default')
101+
assert invocation.execution_plan.complete, invocation.execution_plan.blockers
102+
assert invocation.native_parameters['wf_clone_nxf_home'] == str(homes / 'data/nextflow')
103+
assert 'wf_clone_nxf_home' not in params # No caller-supplied workaround.
104+
_, paths = native.ngs_clone_runtime_dependencies(invocation.native_parameters)
105+
jar = next(path for key, path in paths.items() if key.endswith('-one.jar'))
106+
assert jar == homes / 'data/nextflow/framework/25.10.0/nextflow-25.10.0-one.jar'

0 commit comments

Comments
 (0)