Skip to content

Commit cc85c2a

Browse files
committed
Expose user enabled features as pytest markers
This allows guarding tests behind markers.
1 parent 4fd5d4b commit cc85c2a

18 files changed

Lines changed: 84 additions & 40 deletions

docs/developer/testing.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,32 @@ def test_service_port(server):
146146
assert server.addr("localhost").port(6379).is_reachable
147147
```
148148

149+
### Feature guarding
150+
151+
Some functionality can only be tested when a feature is enabled.
152+
153+
You can mark an invidivual test to be skipped if needed:
154+
155+
```python
156+
@pytest.mark.feature("iop")
157+
def test_ingress_service(server):
158+
service = server.service("iop-core-ingress")
159+
assert service.is_running and service.is_enabled
160+
```
161+
162+
Often it's better to have an entire file dedicated to a feature and mark the entire file as guarded.
163+
164+
```python
165+
pytestmark = pytest.mark.feature("iop")
166+
167+
def test_ingress_service(server):
168+
service = server.service("iop-core-ingress")
169+
assert service.is_running and service.is_enabled
170+
171+
def test_ingress_http_endpoint(server):
172+
# ...
173+
```
174+
149175
### API test
150176

151177
The `foremanapi` fixture is an [apypie](https://github.com/Apipie/apypie) `ForemanApi` client that connects to the deployed Foreman instance(authenticated as `admin`/`changeme`). It maps directly to the Foreman REST API — each method takes a resource name that corresponds to an API endpoint:

tests/conftest.py

Lines changed: 42 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import os
22
import uuid
3+
from functools import cached_property
4+
from pathlib import Path
35

46
import apypie
57
import paramiko
@@ -16,6 +18,38 @@
1618
SSH_CONFIG='./.tmp/ssh-config'
1719

1820

21+
class UserParameters:
22+
def __init__(self, config):
23+
self._config = config
24+
25+
@property
26+
def _obsah_state(self) -> Path:
27+
# mirror what foremanctl / obsah does
28+
if state := os.environ.get('OBSAH_STATE'):
29+
return Path(state)
30+
31+
if base := os.environ.get('OBSAH_BASE'):
32+
root = Path(base)
33+
else:
34+
root = self._config.rootpath
35+
return root / '.var' / 'lib' / 'foremanctl'
36+
37+
def _read_parameters(self):
38+
params_file = self._obsah_state / 'parameters.yaml'
39+
if params_file.exists():
40+
with params_file.open('r') as f:
41+
return yaml.safe_load(f)
42+
43+
return None
44+
45+
@cached_property
46+
def features(self):
47+
params = self._read_parameters()
48+
if params:
49+
return set(params.get('features', []))
50+
return set()
51+
52+
1953
def pytest_addoption(parser):
2054
parser.addoption("--certificate-source", action="store", default="default", choices=('default', 'installer', 'custom_server'), help="Certificate source used during deployment")
2155
parser.addoption("--database-mode", action="store", default="internal", choices=('internal', 'external'), help="Whether the database is internal or external")
@@ -206,34 +240,18 @@ def wait_for_metadata_generate(foremanapi):
206240
wait_for_tasks(foremanapi, 'label = Actions::Katello::Repository::MetadataGenerate')
207241

208242

209-
def is_iop_enabled():
210-
test_dir = os.path.dirname(os.path.abspath(__file__))
211-
foremanctl_dir = os.path.dirname(test_dir)
212-
params_file = os.path.join(foremanctl_dir, '.var', 'lib', 'foremanctl', 'parameters.yaml')
213-
214-
if os.path.exists(params_file):
215-
with open(params_file, 'r') as f:
216-
params = yaml.safe_load(f)
217-
features = params.get('features', [])
218-
if isinstance(features, str):
219-
features = features.split()
220-
return 'iop' in features
221-
222-
return False
223-
224-
225243
def pytest_configure(config):
226-
config.addinivalue_line("markers", "iop: tests requiring IOP to be enabled")
244+
config.addinivalue_line("markers", "feature(name): mark a test as requiring a feature")
227245

246+
config.user_parameters = UserParameters(config)
228247

229-
def pytest_collection_modifyitems(config, items):
230-
if is_iop_enabled():
231-
return
232248

233-
skip_iop = pytest.mark.skip(reason="IOP not enabled - skipping IOP tests ('iop' not in enabled_features)")
234-
for item in items:
235-
if "iop" in item.keywords:
236-
item.add_marker(skip_iop)
249+
def pytest_runtest_setup(item):
250+
feature_markers = set(mark.args[0] for mark in item.iter_markers(name="feature"))
251+
if feature_markers:
252+
missing = feature_markers - item.config.user_parameters.features
253+
if missing:
254+
pytest.skip("test requires feature(s) {!r}".format(missing))
237255

238256

239257
class ResolveAdapter(HTTPAdapter):

tests/iop/test_advisor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import pytest
22

3-
pytestmark = pytest.mark.iop
3+
pytestmark = pytest.mark.feature("iop")
44

55

66
def test_advisor_backend_api_service(server):

tests/iop/test_advisor_frontend.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import pytest
22

3-
pytestmark = pytest.mark.iop
3+
pytestmark = pytest.mark.feature("iop")
44

55

66
def test_advisor_frontend_assets_directory(server):

tests/iop/test_cvemap_downloader.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import pytest
22

3-
pytestmark = pytest.mark.iop
3+
pytestmark = pytest.mark.feature("iop")
44

55

66
def test_cvemap_download_script(server):

tests/iop/test_engine.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import pytest
22

3-
pytestmark = pytest.mark.iop
3+
pytestmark = pytest.mark.feature("iop")
44

55

66
def test_engine_service(server):

tests/iop/test_gateway.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import pytest
22

3-
pytestmark = pytest.mark.iop
3+
pytestmark = pytest.mark.feature("iop")
44

55

66
def test_gateway_service(server):

tests/iop/test_ingress.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import pytest
22

3-
pytestmark = pytest.mark.iop
3+
pytestmark = pytest.mark.feature("iop")
44

55

66
def test_ingress_service(server):

tests/iop/test_integration.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import pytest
22

3-
pytestmark = pytest.mark.iop
3+
pytestmark = pytest.mark.feature("iop")
44

55

66
def test_iop_core_kafka_service(server):

tests/iop/test_inventory.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import pytest
22

3-
pytestmark = pytest.mark.iop
3+
pytestmark = pytest.mark.feature("iop")
44

55

66
def test_inventory_migrate_service(server):

0 commit comments

Comments
 (0)