Skip to content

Commit

Permalink
Move user membership in project tests to test_benchcab
Browse files Browse the repository at this point in the history
  • Loading branch information
abhaasgoyal committed Feb 2, 2024
1 parent b78294b commit e3f1f9e
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 65 deletions.
20 changes: 13 additions & 7 deletions benchcab/benchcab.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,21 @@ def _validate_environment(self, project: str, modules: list):
)
sys.exit(1)

required_groups = [project, "ks32", "hh5"]
if project is None:
msg = """Couldn't resolve project: check 'project' in config.yaml

Check warning on line 68 in benchcab/benchcab.py

View check run for this annotation

Codecov / codecov/patch

benchcab/benchcab.py#L67-L68

Added lines #L67 - L68 were not covered by tests
and/or $PROJECT set in ~/.config/gadi-login.conf
"""
raise AttributeError(msg)

Check warning on line 71 in benchcab/benchcab.py

View check run for this annotation

Codecov / codecov/patch

benchcab/benchcab.py#L71

Added line #L71 was not covered by tests

required_groups = set([project, "ks32", "hh5"])

Check warning on line 73 in benchcab/benchcab.py

View check run for this annotation

Codecov / codecov/patch

benchcab/benchcab.py#L73

Added line #L73 was not covered by tests
groups = [grp.getgrgid(gid).gr_name for gid in os.getgroups()]
if not set(required_groups).issubset(groups):
print(
"Error: user does not have the required group permissions.",
"The required groups are:",
", ".join(required_groups),
if not required_groups.issubset(groups):
msg = (

Check warning on line 76 in benchcab/benchcab.py

View check run for this annotation

Codecov / codecov/patch

benchcab/benchcab.py#L75-L76

Added lines #L75 - L76 were not covered by tests
f"""Error: user does not have the required group permissions.,
The required groups are:,
{", ".join(required_groups)}""",
)
sys.exit(1)
raise PermissionError(msg)

Check warning on line 81 in benchcab/benchcab.py

View check run for this annotation

Codecov / codecov/patch

benchcab/benchcab.py#L81

Added line #L81 was not covered by tests

for modname in modules:
if not self.modules_handler.module_is_avail(modname):
Expand Down
26 changes: 3 additions & 23 deletions benchcab/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,27 +83,7 @@ def read_optional_key(config: dict):
The configuration file with with/without optional keys
"""
if "project" not in config:
if "PROJECT" not in os.environ:
msg = """Couldn't resolve project: check 'project' in config.yaml
and/or $PROJECT set in ~/.config/gadi-login.conf
"""
raise ValueError(msg)
config["project"] = os.environ["PROJECT"]

groups = list(
set(
[
group
for data_dir in internal.USER_PROJECT_DIRS
for group in os.listdir(data_dir)
]
)
)

if config["project"] not in groups:
msg = f"User is not a member of project [{config['project']}]: Check if project key is correct"

raise ValueError(msg)
config["project"] = os.environ.get("PROJECT", None)

if "realisations" in config:
for r in config["realisations"]:
Expand Down Expand Up @@ -166,8 +146,8 @@ def read_config(config_path: str) -> dict:
"""
# Read configuration file
config = read_config_file(config_path)
# Validate and return.
validate_config(config)
# Populate configuration dict with optional keys
read_optional_key(config)
# Validate and return.
validate_config(config)
return config
4 changes: 0 additions & 4 deletions benchcab/internal.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,6 @@
# Path to the user's current working directory
CWD = Path.cwd()

# Directory List is obtained from Gadi User Guide in Section - Gadi Resources
# https://opus.nci.org.au/display/Help/0.+Welcome+to+Gadi
USER_PROJECT_DIRS = ["/g/data", "/scratch"]

# Path to the user's home directory
HOME_DIR = Path(os.environ["HOME"])

Expand Down
3 changes: 1 addition & 2 deletions docs/user_guide/config_options.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ The different running modes of `benchcab` are solely dependent on the options us

## project

NCI project ID to charge the simulations to.
This key is _optional_. If ID is not provided, the current workspace project - i.e. the environment variable `$PROJECT` will be used.
: **Default:** user's default project, _optional key_. :octicons-dash-24: NCI project ID to charge the simulations to. The user's default project defined in the $PROJECT environment variable is used by default.

``` yaml

Expand Down
67 changes: 67 additions & 0 deletions tests/test_benchcab.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
"""`pytest` tests for `benchcab.py`."""
import re
from contextlib import nullcontext as does_not_raise
from unittest import mock

import pytest

from benchcab.benchcab import Benchcab


@pytest.fixture(scope="module", autouse=True)
def _set_user_projects():
with mock.patch("grp.getgrgid") as mocked_getgrid, mock.patch(

Check warning on line 13 in tests/test_benchcab.py

View check run for this annotation

Codecov / codecov/patch

tests/test_benchcab.py#L13

Added line #L13 was not covered by tests
"os.getgroups"
) as mocked_groups:
type(mocked_getgrid.return_value).gr_name = mock.PropertyMock(

Check warning on line 16 in tests/test_benchcab.py

View check run for this annotation

Codecov / codecov/patch

tests/test_benchcab.py#L16

Added line #L16 was not covered by tests
return_value="hh5"
)
mocked_groups.return_value = [1]
yield

Check warning on line 20 in tests/test_benchcab.py

View check run for this annotation

Codecov / codecov/patch

tests/test_benchcab.py#L19-L20

Added lines #L19 - L20 were not covered by tests


@pytest.fixture(scope="module", params=["hh5", "invalid_project_name"])
def config_project(request):
"""Get config project name."""
return request.param

Check warning on line 26 in tests/test_benchcab.py

View check run for this annotation

Codecov / codecov/patch

tests/test_benchcab.py#L26

Added line #L26 was not covered by tests


# Error message if config project name cannot be resolved.
no_project_name_msg = re.escape(
"""Couldn't resolve project: check 'project' in config.yaml
and/or $PROJECT set in ~/.config/gadi-login.conf
"""
)

# For testing whether user is member of necessary projects to run benchcab, we need to simulate the environment of Gadi
# TODO: Simulate Gadi environment for running tests for validating environment


@pytest.mark.skip()
@pytest.mark.parametrize(
("config_project", "pytest_error"),
[
("hh5", does_not_raise()),
(None, pytest.raises(AttributeError, match=no_project_name_msg)),
],
)
def test_project_name(config_project, pytest_error):
"""Tests whether config project name is suitable to run in Gadi environment."""
app = Benchcab(benchcab_exe_path=None)
with pytest_error:
app._validate_environment(project=config_project, modules=[])

Check warning on line 52 in tests/test_benchcab.py

View check run for this annotation

Codecov / codecov/patch

tests/test_benchcab.py#L50-L52

Added lines #L50 - L52 were not covered by tests


@pytest.mark.skip()
@pytest.mark.parametrize(
("config_project", "pytest_error"),
[
("hh5", does_not_raise()),
("invalid_project_name", pytest.raises(PermissionError)),
],
)
def test_user_project_group(config_project, pytest_error):
"""Test _validate_environment for if current user's groups does not contain the project name."""
app = Benchcab(benchcab_exe_path=None)
with pytest_error:
app._validate_environment(project=config_project, modules=[])

Check warning on line 67 in tests/test_benchcab.py

View check run for this annotation

Codecov / codecov/patch

tests/test_benchcab.py#L65-L67

Added lines #L65 - L67 were not covered by tests
42 changes: 13 additions & 29 deletions tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,6 @@


# Temporarily set $PROJECT for testing module
@pytest.fixture(autouse=True, scope="module")
def _set_project_validation_dirs():
with mock.patch("os.listdir") as mocked_listdir:
mocked_listdir.return_value = [
NO_OPTIONAL_CONFIG_PROJECT,
OPTIONAL_CONFIG_PROJECT,
]
yield


@pytest.fixture(autouse=True)
def _set_project_env_variable(monkeypatch):
# Clear existing environment variables first
Expand Down Expand Up @@ -162,6 +152,13 @@ def test_validate_config(config_str, pytest_error):
class TestReadOptionalKey:
"""Tests related to adding optional keys in config."""

@pytest.fixture()
def all_optional_default_config_no_project(
self, all_optional_default_config
) -> dict:
"""Set project keyword to None."""
return all_optional_default_config | {"project": None}

@pytest.mark.parametrize(
("input_config", "output_config"),
[
Expand All @@ -176,28 +173,15 @@ def test_read_optional_key_add_data(self, input_config, output_config, request):
bc.read_optional_key(config)
assert pformat(config) == pformat(request.getfixturevalue(output_config))

def test_no_project_name(self, no_optional_config, monkeypatch):
def test_no_project_name(
self, no_optional_config, all_optional_default_config_no_project, monkeypatch
):
"""If project key and $PROJECT are not provided, then raise error."""
monkeypatch.delenv("PROJECT")
err_msg = re.escape(
"""Couldn't resolve project: check 'project' in config.yaml
and/or $PROJECT set in ~/.config/gadi-login.conf
"""
)
with pytest.raises(ValueError, match=err_msg):
bc.read_optional_key(no_optional_config)

def test_user_not_in_project(self, no_optional_config):
"""If user is not in viewable NCI projects, raise error."""
no_optional_config["project"] = "non_existing"
err_msg = re.escape(
"User is not a member of project [non_existing]: Check if project key is correct"
bc.read_optional_key(no_optional_config)
assert pformat(no_optional_config) == pformat(
all_optional_default_config_no_project
)
with pytest.raises(
ValueError,
match=err_msg,
):
bc.read_optional_key(no_optional_config)


@pytest.mark.parametrize(
Expand Down

0 comments on commit e3f1f9e

Please sign in to comment.