Skip to content

Commit 814e27a

Browse files
authored
ENH: Adapt to stricter project .fmu init behavior (#333)
1 parent 262f2de commit 814e27a

File tree

8 files changed

+287
-175
lines changed

8 files changed

+287
-175
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ dynamic = ["version"]
3232
dependencies = [
3333
"fastapi",
3434
"fmu-datamodels",
35-
"fmu-settings>=0.26.0",
35+
"fmu-settings>=0.28.0",
3636
"httpx",
3737
"packaging",
3838
"pydantic",

src/fmu_settings_api/v1/routes/project.py

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
from fmu.datamodels.fmu_results.fields import Model
1616
from fmu.settings import CacheResource, ProjectFMUDirectory
1717
from fmu.settings._global_config import InvalidGlobalConfigurationError
18+
from fmu.settings._init import (
19+
REQUIRED_FMU_PROJECT_SUBDIRS,
20+
InvalidFMUProjectPathError,
21+
)
1822
from fmu.settings.models.change_info import ChangeInfo
1923
from fmu.settings.models.diff import ResourceDiff
2024
from fmu.settings.models.log import Log
@@ -60,6 +64,9 @@
6064
)
6165

6266
router = APIRouter(prefix="/project", tags=["project"])
67+
REQUIRED_PROJECT_DIRS_TEXT = ", ".join(
68+
f"'{dir_name}'" for dir_name in REQUIRED_FMU_PROJECT_SUBDIRS
69+
)
6370

6471
ProjectResponses: Final[Responses] = {
6572
**inline_add_response(
@@ -104,6 +111,24 @@
104111
),
105112
}
106113

114+
ProjectInitResponses: Final[Responses] = {
115+
**ProjectResponses,
116+
**ProjectExistsResponses,
117+
**inline_add_response(
118+
422,
119+
"The requested path exists but is not a valid FMU project root.",
120+
[
121+
{
122+
"detail": (
123+
"Failed initializing .fmu directory. Initialize it from a "
124+
f"project root containing {REQUIRED_PROJECT_DIRS_TEXT}. "
125+
"Did not find: {missing_project_dirs}."
126+
),
127+
},
128+
],
129+
),
130+
}
131+
107132
LockConflictResponses: Final[Responses] = {
108133
**inline_add_response(
109134
423,
@@ -481,8 +506,7 @@ async def post_project(
481506
),
482507
responses={
483508
**GetSessionResponses,
484-
**ProjectResponses,
485-
**ProjectExistsResponses,
509+
**ProjectInitResponses,
486510
},
487511
)
488512
async def post_init_project(
@@ -504,6 +528,8 @@ async def post_init_project(
504528
raise HTTPException(
505529
status_code=404, detail=f"Path {path} does not exist"
506530
) from e
531+
except InvalidFMUProjectPathError as e:
532+
raise HTTPException(status_code=422, detail=str(e)) from e
507533
except FileExistsError as e:
508534
raise HTTPException(
509535
status_code=409, detail=f".fmu already exists at {path}"

tests/conftest.py

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@
1919
)
2020
from fmu.settings import ProjectFMUDirectory
2121
from fmu.settings._fmu_dir import UserFMUDirectory
22-
from fmu.settings._init import init_fmu_directory, init_user_fmu_directory
22+
from fmu.settings._init import (
23+
REQUIRED_FMU_PROJECT_SUBDIRS,
24+
init_fmu_directory,
25+
init_user_fmu_directory,
26+
)
2327
from pytest import MonkeyPatch
2428

2529
from fmu_settings_api.__main__ import app
@@ -29,6 +33,31 @@
2933
from fmu_settings_api.session import SessionManager, add_fmu_project_to_session
3034

3135

36+
@pytest.fixture
37+
def make_fmu_project_root() -> Callable[[Path], Path]:
38+
"""Return a helper that prepares a valid FMU project root for a test path."""
39+
40+
def _make_fmu_project_root(path: Path) -> Path:
41+
path.mkdir(parents=True, exist_ok=True)
42+
for dir_name in REQUIRED_FMU_PROJECT_SUBDIRS:
43+
(path / dir_name).mkdir(parents=True, exist_ok=True)
44+
return path
45+
46+
return _make_fmu_project_root
47+
48+
49+
@pytest.fixture
50+
def init_project_fmu_directory(
51+
make_fmu_project_root: Callable[[Path], Path],
52+
) -> Callable[[Path], ProjectFMUDirectory]:
53+
"""Return a helper that initializes a valid project .fmu directory."""
54+
55+
def _init_project_fmu_directory(path: Path) -> ProjectFMUDirectory:
56+
return init_fmu_directory(make_fmu_project_root(path))
57+
58+
return _init_project_fmu_directory
59+
60+
3261
@pytest.fixture
3362
def create_stratigraphic_unit() -> Callable[..., StratigraphicUnit]:
3463
"""Fixture that returns a helper function to create StratigraphicUnit.
@@ -153,9 +182,11 @@ def mock_token() -> str:
153182

154183

155184
@pytest.fixture
156-
def fmu_dir(tmp_path: Path) -> ProjectFMUDirectory:
185+
def fmu_dir(
186+
tmp_path: Path, make_fmu_project_root: Callable[[Path], Path]
187+
) -> ProjectFMUDirectory:
157188
"""Creates a .fmu directory in a tmp path."""
158-
return init_fmu_directory(tmp_path)
189+
return init_fmu_directory(make_fmu_project_root(tmp_path))
159190

160191

161192
@pytest.fixture
@@ -193,7 +224,11 @@ def user_fmu_dir_no_permissions(fmu_dir_path: Path) -> Generator[Path]:
193224

194225

195226
@pytest.fixture
196-
def tmp_path_mocked_home(tmp_path: Path, monkeypatch: MonkeyPatch) -> Generator[Path]:
227+
def tmp_path_mocked_home(
228+
tmp_path: Path,
229+
monkeypatch: MonkeyPatch,
230+
make_fmu_project_root: Callable[[Path], Path],
231+
) -> Generator[Path]:
197232
"""Mocks Path.home() for routes that depend on UserFMUDirectory.
198233
199234
This mocks the user .fmu into tmp_path/home/.fmu.
@@ -203,6 +238,7 @@ def tmp_path_mocked_home(tmp_path: Path, monkeypatch: MonkeyPatch) -> Generator[
203238
"""
204239
mocked_user_home = tmp_path / "home"
205240
mocked_user_home.mkdir()
241+
make_fmu_project_root(tmp_path)
206242
with patch("pathlib.Path.home", return_value=mocked_user_home):
207243
monkeypatch.chdir(tmp_path)
208244
yield tmp_path

0 commit comments

Comments
 (0)