Skip to content

Commit d050598

Browse files
Endpoint tests with Sumo data (#710)
Co-authored-by: Anders Fredrik Kiær <[email protected]>
1 parent eac7ffb commit d050598

File tree

16 files changed

+308
-96
lines changed

16 files changed

+308
-96
lines changed
+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
name: integration
2+
3+
on:
4+
push:
5+
workflow_dispatch:
6+
schedule:
7+
- cron: "0 0 * * *"
8+
9+
jobs:
10+
sumo_prod:
11+
runs-on: ubuntu-latest
12+
permissions:
13+
contents: read
14+
id-token: write
15+
16+
steps:
17+
- uses: actions/checkout@v4
18+
19+
- name: 🤖 Get shared key from Sumo
20+
working-directory: ./backend_py/primary
21+
env:
22+
SHARED_KEY_SUMO_PROD: ${{ secrets.SHARED_KEY_DROGON_READ_PROD }}
23+
run: |
24+
if [ ${#SHARED_KEY_SUMO_PROD} -eq 0 ]; then
25+
echo "Error: SHARED_KEY_SUMO_PROD is empty. Stopping the action."
26+
exit 1
27+
fi
28+
mkdir ~/.sumo
29+
echo $SHARED_KEY_SUMO_PROD > ~/.sumo/9e5443dd-3431-4690-9617-31eed61cb55a.sharedkey
30+
31+
- name: 🐍 Set up Python
32+
uses: actions/setup-python@v4
33+
with:
34+
python-version: "3.11"
35+
cache: pip
36+
37+
- name: 📦 Install poetry and dependencies
38+
working-directory: ./backend_py/primary
39+
run: |
40+
pip install --upgrade pip
41+
pip install poetry==1.8.5 # Pin Poetry to version 1.8.5
42+
poetry config virtualenvs.create false
43+
poetry check --lock # Check lock file is consistent with pyproject.toml
44+
poetry install --with dev
45+
46+
- name: 🤖 Run tests
47+
working-directory: ./backend_py/primary
48+
env:
49+
WEBVIZ_CLIENT_SECRET: 0
50+
WEBVIZ_SMDA_SUBSCRIPTION_KEY: 0
51+
WEBVIZ_SMDA_RESOURCE_SCOPE: 0
52+
WEBVIZ_VDS_HOST_ADDRESS: 0
53+
WEBVIZ_ENTERPRISE_SUBSCRIPTION_KEY: 0
54+
WEBVIZ_SSDL_RESOURCE_SCOPE: 0
55+
WEBVIZ_SUMU_ENV: prod
56+
run: |
57+
pytest -s --timeout=300 ./tests/integration

.github/workflows/webviz.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ jobs:
106106
black --check primary/ tests/
107107
pylint primary/ tests/
108108
bandit --recursive primary/
109-
mypy primary/ tests/
109+
mypy primary/
110110
111111
- name: 🤖 Run tests
112112
working-directory: ./backend_py/primary

backend_py/primary/poetry.lock

+45-12
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend_py/primary/primary/main.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from primary.auth.enforce_logged_in_middleware import EnforceLoggedInMiddleware
1414
from primary.middleware.add_process_time_to_server_timing_middleware import AddProcessTimeToServerTimingMiddleware
1515
from primary.routers.dev.router import router as dev_router
16-
from primary.routers.explore import router as explore_router
16+
from primary.routers.explore.router import router as explore_router
1717
from primary.routers.general import router as general_router
1818
from primary.routers.graph.router import router as graph_router
1919
from primary.routers.grid3d.router import router as grid3d_router

backend_py/primary/primary/routers/explore.py backend_py/primary/primary/routers/explore/router.py

+12-38
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,27 @@
1-
from typing import List, Sequence
1+
from typing import List
22

33
from fastapi import APIRouter, Depends, Path, Query
4-
from pydantic import BaseModel
54

65
from primary.auth.auth_helper import AuthHelper
76
from primary.services.sumo_access.case_inspector import CaseInspector
87
from primary.services.sumo_access.sumo_inspector import SumoInspector
98
from primary.services.utils.authenticated_user import AuthenticatedUser
109

11-
router = APIRouter()
12-
13-
14-
class FieldInfo(BaseModel):
15-
field_identifier: str
16-
17-
18-
class CaseInfo(BaseModel):
19-
uuid: str
20-
name: str
21-
status: str
22-
user: str
10+
from . import schemas
2311

24-
25-
class EnsembleInfo(BaseModel):
26-
name: str
27-
realization_count: int
28-
29-
30-
class EnsembleDetails(BaseModel):
31-
name: str
32-
field_identifier: str
33-
stratigraphic_column_identifier: str
34-
case_name: str
35-
case_uuid: str
36-
realizations: Sequence[int]
12+
router = APIRouter()
3713

3814

3915
@router.get("/fields")
4016
async def get_fields(
4117
authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user),
42-
) -> List[FieldInfo]:
18+
) -> List[schemas.FieldInfo]:
4319
"""
4420
Get list of fields
4521
"""
4622
sumo_inspector = SumoInspector(authenticated_user.get_sumo_access_token())
4723
field_ident_arr = await sumo_inspector.get_fields_async()
48-
ret_arr = [FieldInfo(field_identifier=field_ident.identifier) for field_ident in field_ident_arr]
24+
ret_arr = [schemas.FieldInfo(field_identifier=field_ident.identifier) for field_ident in field_ident_arr]
4925

5026
return ret_arr
5127

@@ -54,14 +30,14 @@ async def get_fields(
5430
async def get_cases(
5531
authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user),
5632
field_identifier: str = Query(description="Field identifier"),
57-
) -> List[CaseInfo]:
33+
) -> List[schemas.CaseInfo]:
5834
"""Get list of cases for specified field"""
5935
sumo_inspector = SumoInspector(authenticated_user.get_sumo_access_token())
6036
case_info_arr = await sumo_inspector.get_cases_async(field_identifier=field_identifier)
6137

62-
ret_arr: List[CaseInfo] = []
38+
ret_arr: List[schemas.CaseInfo] = []
6339

64-
ret_arr = [CaseInfo(uuid=ci.uuid, name=ci.name, status=ci.status, user=ci.user) for ci in case_info_arr]
40+
ret_arr = [schemas.CaseInfo(uuid=ci.uuid, name=ci.name, status=ci.status, user=ci.user) for ci in case_info_arr]
6541

6642
return ret_arr
6743

@@ -70,22 +46,20 @@ async def get_cases(
7046
async def get_ensembles(
7147
authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user),
7248
case_uuid: str = Path(description="Sumo case uuid"),
73-
) -> List[EnsembleInfo]:
49+
) -> List[schemas.EnsembleInfo]:
7450
"""Get list of ensembles for a case"""
7551
case_inspector = CaseInspector.from_case_uuid(authenticated_user.get_sumo_access_token(), case_uuid)
7652
iteration_info_arr = await case_inspector.get_iterations_async()
7753

78-
print(iteration_info_arr)
79-
80-
return [EnsembleInfo(name=it.name, realization_count=it.realization_count) for it in iteration_info_arr]
54+
return [schemas.EnsembleInfo(name=it.name, realization_count=it.realization_count) for it in iteration_info_arr]
8155

8256

8357
@router.get("/cases/{case_uuid}/ensembles/{ensemble_name}")
8458
async def get_ensemble_details(
8559
authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user),
8660
case_uuid: str = Path(description="Sumo case uuid"),
8761
ensemble_name: str = Path(description="Ensemble name"),
88-
) -> EnsembleDetails:
62+
) -> schemas.EnsembleDetails:
8963
"""Get more detailed information for an ensemble"""
9064

9165
case_inspector = CaseInspector.from_case_uuid(authenticated_user.get_sumo_access_token(), case_uuid)
@@ -97,7 +71,7 @@ async def get_ensemble_details(
9771
if len(field_identifiers) != 1:
9872
raise NotImplementedError("Multiple field identifiers not supported")
9973

100-
return EnsembleDetails(
74+
return schemas.EnsembleDetails(
10175
name=ensemble_name,
10276
case_name=case_name,
10377
case_uuid=case_uuid,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
from typing import Sequence
2+
3+
from pydantic import BaseModel
4+
5+
6+
class FieldInfo(BaseModel):
7+
field_identifier: str
8+
9+
10+
class CaseInfo(BaseModel):
11+
uuid: str
12+
name: str
13+
status: str
14+
user: str
15+
16+
17+
class EnsembleInfo(BaseModel):
18+
name: str
19+
realization_count: int
20+
21+
22+
class EnsembleDetails(BaseModel):
23+
name: str
24+
field_identifier: str
25+
case_name: str
26+
case_uuid: str
27+
realizations: Sequence[int]
28+
stratigraphic_column_identifier: str

backend_py/primary/primary/services/sumo_access/_helpers.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@
1111

1212

1313
def create_sumo_client(access_token: str) -> SumoClient:
14-
sumo_client = SumoClient(env=config.SUMO_ENV, token=access_token, interactive=False)
14+
if access_token == "DUMMY_TOKEN_FOR_TESTING": # nosec bandit B105
15+
sumo_client = SumoClient(env=config.SUMO_ENV, interactive=False)
16+
else:
17+
sumo_client = SumoClient(env=config.SUMO_ENV, token=access_token, interactive=False)
1518
return sumo_client
1619

1720

backend_py/primary/primary/utils/azure_monitor_setup.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from fastapi import FastAPI
44
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
55
from opentelemetry.sdk._logs import LoggingHandler
6-
6+
from opentelemetry._logs import get_logger
77

88
# This is a custom logging handler that does formatting of log messages before passing them on to open telemetry.
99
# Note that the class we're inheriting from here *is* an OpenTelemetry derived Python logger.
@@ -18,8 +18,10 @@ def emit(self, record: logging.LogRecord) -> None:
1818
record.msg = formatted_msg
1919
record.args = None
2020

21+
logger = get_logger(record.name, logger_provider=self._logger_provider)
22+
2123
# Note that the logger that we're calling emit on here is an Open Telemetry Logger, not a Python logger.
22-
self._logger.emit(self._translate(record))
24+
logger.emit(self._translate(record))
2325

2426
# For inspecting and debugging the actual telemetry payload, uncomment the following lines.
2527
# log_record_as_json = self._translate(record).to_json()

backend_py/primary/pyproject.toml

+6-3
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ pandas = {version = "2.0.1", extras = ["performance"]}
2222
httpx = "^0.24.0"
2323
fmu-sumo = "1.2.5"
2424
sumo-wrapper-python = "1.0.9"
25-
azure-monitor-opentelemetry = "^1.1.0"
25+
azure-monitor-opentelemetry = "1.1.0"
2626
requests-toolbelt = "^1.0.0"
2727
pottery = "^3.0.0"
2828
xtgeo = "^3.8.0"
@@ -33,11 +33,13 @@ polars = "^1.6.0"
3333
[tool.poetry.group.dev.dependencies]
3434
black = "^22.12.0"
3535
pylint = "^2.15.10"
36-
pytest = "^7.2.1"
36+
pytest = "^8.3.2"
3737
mypy = "^1.9.0"
3838
bandit = "^1.7.5"
3939
types-requests = "^2.31.0.1"
4040
types-redis = "^4.6.0"
41+
pytest-timeout = "^2.3.1"
42+
pytest-asyncio = "^0.24.0"
4143

4244

4345
[build-system]
@@ -64,5 +66,6 @@ disallow_untyped_defs = true
6466
[tool.pytest.ini_options]
6567
pythonpath = ["."]
6668
filterwarnings = "ignore::DeprecationWarning:pkg_resources"
67-
69+
asyncio_mode="auto"
70+
asyncio_default_fixture_loop_scope="session"
6871

0 commit comments

Comments
 (0)