diracx-testing 0.0.1a23__tar.gz → 0.0.1a25__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {diracx_testing-0.0.1a23 → diracx_testing-0.0.1a25}/PKG-INFO +2 -2
- diracx_testing-0.0.1a25/src/diracx/testing/__init__.py +47 -0
- diracx_testing-0.0.1a25/src/diracx/testing/entrypoints.py +67 -0
- {diracx_testing-0.0.1a23 → diracx_testing-0.0.1a25}/src/diracx/testing/mock_osdb.py +19 -14
- diracx_testing-0.0.1a23/src/diracx/testing/__init__.py → diracx_testing-0.0.1a25/src/diracx/testing/utils.py +24 -71
- {diracx_testing-0.0.1a23 → diracx_testing-0.0.1a25}/src/diracx_testing.egg-info/PKG-INFO +2 -2
- {diracx_testing-0.0.1a23 → diracx_testing-0.0.1a25}/src/diracx_testing.egg-info/SOURCES.txt +2 -0
- {diracx_testing-0.0.1a23 → diracx_testing-0.0.1a25}/README.md +0 -0
- {diracx_testing-0.0.1a23 → diracx_testing-0.0.1a25}/pyproject.toml +0 -0
- {diracx_testing-0.0.1a23 → diracx_testing-0.0.1a25}/setup.cfg +0 -0
- {diracx_testing-0.0.1a23 → diracx_testing-0.0.1a25}/src/diracx/testing/dummy_osdb.py +0 -0
- {diracx_testing-0.0.1a23 → diracx_testing-0.0.1a25}/src/diracx/testing/osdb.py +0 -0
- {diracx_testing-0.0.1a23 → diracx_testing-0.0.1a25}/src/diracx/testing/routers.py +0 -0
- {diracx_testing-0.0.1a23 → diracx_testing-0.0.1a25}/src/diracx_testing.egg-info/dependency_links.txt +0 -0
- {diracx_testing-0.0.1a23 → diracx_testing-0.0.1a25}/src/diracx_testing.egg-info/requires.txt +0 -0
- {diracx_testing-0.0.1a23 → diracx_testing-0.0.1a25}/src/diracx_testing.egg-info/top_level.txt +0 -0
@@ -0,0 +1,47 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from .entrypoints import verify_entry_points
|
4
|
+
from .utils import (
|
5
|
+
ClientFactory,
|
6
|
+
aio_moto,
|
7
|
+
cli_env,
|
8
|
+
client_factory,
|
9
|
+
demo_dir,
|
10
|
+
demo_kubectl_env,
|
11
|
+
demo_urls,
|
12
|
+
do_device_flow_with_dex,
|
13
|
+
fernet_key,
|
14
|
+
private_key_pem,
|
15
|
+
pytest_addoption,
|
16
|
+
pytest_collection_modifyitems,
|
17
|
+
session_client_factory,
|
18
|
+
test_auth_settings,
|
19
|
+
test_dev_settings,
|
20
|
+
test_login,
|
21
|
+
test_sandbox_settings,
|
22
|
+
with_cli_login,
|
23
|
+
with_config_repo,
|
24
|
+
)
|
25
|
+
|
26
|
+
__all__ = (
|
27
|
+
"verify_entry_points",
|
28
|
+
"ClientFactory",
|
29
|
+
"do_device_flow_with_dex",
|
30
|
+
"test_login",
|
31
|
+
"pytest_addoption",
|
32
|
+
"pytest_collection_modifyitems",
|
33
|
+
"private_key_pem",
|
34
|
+
"fernet_key",
|
35
|
+
"test_dev_settings",
|
36
|
+
"test_auth_settings",
|
37
|
+
"aio_moto",
|
38
|
+
"test_sandbox_settings",
|
39
|
+
"session_client_factory",
|
40
|
+
"client_factory",
|
41
|
+
"with_config_repo",
|
42
|
+
"demo_dir",
|
43
|
+
"demo_urls",
|
44
|
+
"demo_kubectl_env",
|
45
|
+
"cli_env",
|
46
|
+
"with_cli_login",
|
47
|
+
)
|
@@ -0,0 +1,67 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import tomllib
|
4
|
+
from collections import defaultdict
|
5
|
+
from importlib.metadata import PackageNotFoundError, distribution, entry_points
|
6
|
+
|
7
|
+
import pytest
|
8
|
+
|
9
|
+
|
10
|
+
def get_installed_entry_points():
|
11
|
+
"""Retrieve the installed entry points from the environment."""
|
12
|
+
entry_pts = entry_points()
|
13
|
+
diracx_eps = defaultdict(dict)
|
14
|
+
for group in entry_pts.groups:
|
15
|
+
if "diracx" in group:
|
16
|
+
for ep in entry_pts.select(group=group):
|
17
|
+
diracx_eps[group][ep.name] = ep.value
|
18
|
+
return dict(diracx_eps)
|
19
|
+
|
20
|
+
|
21
|
+
def get_entry_points_from_toml(toml_file):
|
22
|
+
"""Parse entry points from pyproject.toml."""
|
23
|
+
with open(toml_file, "rb") as f:
|
24
|
+
pyproject = tomllib.load(f)
|
25
|
+
package_name = pyproject["project"]["name"]
|
26
|
+
return package_name, pyproject.get("project", {}).get("entry-points", {})
|
27
|
+
|
28
|
+
|
29
|
+
def get_current_entry_points(repo_base) -> bool:
|
30
|
+
"""Create current entry points dict for comparison."""
|
31
|
+
current_eps = {}
|
32
|
+
for toml_file in repo_base.glob("diracx-*/pyproject.toml"):
|
33
|
+
package_name, entry_pts = get_entry_points_from_toml(f"{toml_file}")
|
34
|
+
# Ignore packages that are not installed
|
35
|
+
try:
|
36
|
+
distribution(package_name)
|
37
|
+
except PackageNotFoundError:
|
38
|
+
continue
|
39
|
+
# Merge the entry points
|
40
|
+
for key, value in entry_pts.items():
|
41
|
+
current_eps[key] = current_eps.get(key, {}) | value
|
42
|
+
return current_eps
|
43
|
+
|
44
|
+
|
45
|
+
@pytest.fixture(scope="session", autouse=True)
|
46
|
+
def verify_entry_points(request, pytestconfig):
|
47
|
+
try:
|
48
|
+
ini_toml_name = tomllib.loads(pytestconfig.inipath.read_text())["project"][
|
49
|
+
"name"
|
50
|
+
]
|
51
|
+
except tomllib.TOMLDecodeError:
|
52
|
+
return
|
53
|
+
if ini_toml_name == "diracx":
|
54
|
+
repo_base = pytestconfig.inipath.parent
|
55
|
+
elif ini_toml_name.startswith("diracx-"):
|
56
|
+
repo_base = pytestconfig.inipath.parent.parent
|
57
|
+
else:
|
58
|
+
return
|
59
|
+
|
60
|
+
installed_eps = get_installed_entry_points()
|
61
|
+
current_eps = get_current_entry_points(repo_base)
|
62
|
+
|
63
|
+
if installed_eps != current_eps:
|
64
|
+
pytest.fail(
|
65
|
+
"Project and installed entry-points are not consistent. "
|
66
|
+
"You should run `pip install -r requirements-dev.txt`",
|
67
|
+
)
|
@@ -42,8 +42,8 @@ class MockOSDBMixin:
|
|
42
42
|
from diracx.db.sql.utils import DateNowColumn
|
43
43
|
|
44
44
|
# Dynamically create a subclass of BaseSQLDB so we get clearer errors
|
45
|
-
|
46
|
-
self._sql_db =
|
45
|
+
mocked_db = type(f"Mocked{self.__class__.__name__}", (sql_utils.BaseSQLDB,), {})
|
46
|
+
self._sql_db = mocked_db(connection_kwargs["sqlalchemy_dsn"])
|
47
47
|
|
48
48
|
# Dynamically create the table definition based on the fields
|
49
49
|
columns = [
|
@@ -53,16 +53,16 @@ class MockOSDBMixin:
|
|
53
53
|
for field, field_type in self.fields.items():
|
54
54
|
match field_type["type"]:
|
55
55
|
case "date":
|
56
|
-
|
56
|
+
column_type = DateNowColumn
|
57
57
|
case "long":
|
58
|
-
|
58
|
+
column_type = partial(Column, type_=Integer)
|
59
59
|
case "keyword":
|
60
|
-
|
60
|
+
column_type = partial(Column, type_=String(255))
|
61
61
|
case "text":
|
62
|
-
|
62
|
+
column_type = partial(Column, type_=String(64 * 1024))
|
63
63
|
case _:
|
64
64
|
raise NotImplementedError(f"Unknown field type: {field_type=}")
|
65
|
-
columns.append(
|
65
|
+
columns.append(column_type(field, default=None))
|
66
66
|
self._sql_db.metadata = MetaData()
|
67
67
|
self._table = Table("dummy", self._sql_db.metadata, *columns)
|
68
68
|
|
@@ -72,18 +72,22 @@ class MockOSDBMixin:
|
|
72
72
|
yield
|
73
73
|
|
74
74
|
async def __aenter__(self):
|
75
|
-
|
75
|
+
"""Enter the request context.
|
76
|
+
|
77
|
+
This is a no-op as the real OpenSearch class doesn't use transactions.
|
78
|
+
Instead we enter a transaction in each method that needs it.
|
79
|
+
"""
|
76
80
|
return self
|
77
81
|
|
78
82
|
async def __aexit__(self, exc_type, exc_value, traceback):
|
79
|
-
|
83
|
+
pass
|
80
84
|
|
81
85
|
async def create_index_template(self) -> None:
|
82
86
|
async with self._sql_db.engine.begin() as conn:
|
83
87
|
await conn.run_sync(self._sql_db.metadata.create_all)
|
84
88
|
|
85
89
|
async def upsert(self, doc_id, document) -> None:
|
86
|
-
async with self:
|
90
|
+
async with self._sql_db:
|
87
91
|
values = {}
|
88
92
|
for key, value in document.items():
|
89
93
|
if key in self.fields:
|
@@ -106,7 +110,7 @@ class MockOSDBMixin:
|
|
106
110
|
per_page: int = 100,
|
107
111
|
page: int | None = None,
|
108
112
|
) -> tuple[int, list[dict[Any, Any]]]:
|
109
|
-
async with self:
|
113
|
+
async with self._sql_db:
|
110
114
|
# Apply selection
|
111
115
|
if parameters:
|
112
116
|
columns = []
|
@@ -150,7 +154,8 @@ class MockOSDBMixin:
|
|
150
154
|
return results
|
151
155
|
|
152
156
|
async def ping(self):
|
153
|
-
|
157
|
+
async with self._sql_db:
|
158
|
+
return await self._sql_db.ping()
|
154
159
|
|
155
160
|
|
156
161
|
def fake_available_osdb_implementations(name, *, real_available_implementations):
|
@@ -158,6 +163,6 @@ def fake_available_osdb_implementations(name, *, real_available_implementations)
|
|
158
163
|
|
159
164
|
# Dynamically generate a class that inherits from the first implementation
|
160
165
|
# but that also has the MockOSDBMixin
|
161
|
-
|
166
|
+
mock_parameter_db = type(name, (MockOSDBMixin, implementations[0]), {})
|
162
167
|
|
163
|
-
return [
|
168
|
+
return [mock_parameter_db] + implementations
|
@@ -1,3 +1,5 @@
|
|
1
|
+
"""Utilities for testing DiracX."""
|
2
|
+
|
1
3
|
from __future__ import annotations
|
2
4
|
|
3
5
|
# TODO: this needs a lot of documentation, in particular what will matter for users
|
@@ -8,14 +10,11 @@ import os
|
|
8
10
|
import re
|
9
11
|
import ssl
|
10
12
|
import subprocess
|
11
|
-
import tomllib
|
12
|
-
from collections import defaultdict
|
13
13
|
from datetime import datetime, timedelta, timezone
|
14
14
|
from functools import partial
|
15
15
|
from html.parser import HTMLParser
|
16
|
-
from importlib.metadata import PackageNotFoundError, distribution, entry_points
|
17
16
|
from pathlib import Path
|
18
|
-
from typing import TYPE_CHECKING
|
17
|
+
from typing import TYPE_CHECKING, Generator
|
19
18
|
from urllib.parse import parse_qs, urljoin, urlparse
|
20
19
|
from uuid import uuid4
|
21
20
|
|
@@ -83,14 +82,16 @@ def fernet_key() -> str:
|
|
83
82
|
|
84
83
|
|
85
84
|
@pytest.fixture(scope="session")
|
86
|
-
def test_dev_settings() -> DevelopmentSettings:
|
85
|
+
def test_dev_settings() -> Generator[DevelopmentSettings, None, None]:
|
87
86
|
from diracx.core.settings import DevelopmentSettings
|
88
87
|
|
89
88
|
yield DevelopmentSettings()
|
90
89
|
|
91
90
|
|
92
91
|
@pytest.fixture(scope="session")
|
93
|
-
def test_auth_settings(
|
92
|
+
def test_auth_settings(
|
93
|
+
private_key_pem, fernet_key
|
94
|
+
) -> Generator[AuthSettings, None, None]:
|
94
95
|
from diracx.routers.utils.users import AuthSettings
|
95
96
|
|
96
97
|
yield AuthSettings(
|
@@ -169,11 +170,13 @@ class ClientFactory:
|
|
169
170
|
class AlwaysAllowAccessPolicy(BaseAccessPolicy):
|
170
171
|
"""Dummy access policy."""
|
171
172
|
|
173
|
+
@staticmethod
|
172
174
|
async def policy(
|
173
175
|
policy_name: str, user_info: AuthorizedUserInfo, /, **kwargs
|
174
176
|
):
|
175
177
|
pass
|
176
178
|
|
179
|
+
@staticmethod
|
177
180
|
def enrich_tokens(access_payload: dict, refresh_payload: dict):
|
178
181
|
|
179
182
|
return {"PolicySpecific": "OpenAccessForTest"}, {}
|
@@ -249,6 +252,7 @@ class ClientFactory:
|
|
249
252
|
assert (
|
250
253
|
self.app.dependency_overrides == {} and self.app.lifetime_functions == []
|
251
254
|
), "configure cannot be nested"
|
255
|
+
|
252
256
|
for k, v in self.all_dependency_overrides.items():
|
253
257
|
|
254
258
|
class_name = k.__self__.__name__
|
@@ -281,17 +285,26 @@ class ClientFactory:
|
|
281
285
|
import sqlalchemy
|
282
286
|
from sqlalchemy.util.concurrency import greenlet_spawn
|
283
287
|
|
288
|
+
from diracx.db.os.utils import BaseOSDB
|
284
289
|
from diracx.db.sql.utils import BaseSQLDB
|
290
|
+
from diracx.testing.mock_osdb import MockOSDBMixin
|
285
291
|
|
286
292
|
for k, v in self.app.dependency_overrides.items():
|
287
|
-
# Ignore dependency overrides which aren't BaseSQLDB.transaction
|
288
|
-
if (
|
289
|
-
|
290
|
-
|
293
|
+
# Ignore dependency overrides which aren't BaseSQLDB.transaction or BaseOSDB.session
|
294
|
+
if isinstance(v, UnavailableDependency) or k.__func__ not in (
|
295
|
+
BaseSQLDB.transaction.__func__,
|
296
|
+
BaseOSDB.session.__func__,
|
291
297
|
):
|
298
|
+
|
292
299
|
continue
|
300
|
+
|
293
301
|
# The first argument of the overridden BaseSQLDB.transaction is the DB object
|
294
302
|
db = v.args[0]
|
303
|
+
# We expect the OS DB to be mocked with sqlite, so use the
|
304
|
+
# internal DB
|
305
|
+
if isinstance(db, MockOSDBMixin):
|
306
|
+
db = db._sql_db
|
307
|
+
|
295
308
|
assert isinstance(db, BaseSQLDB), (k, db)
|
296
309
|
|
297
310
|
# set PRAGMA foreign_keys=ON if sqlite
|
@@ -638,7 +651,7 @@ async def test_login(monkeypatch, capfd, cli_env):
|
|
638
651
|
# Run the login command
|
639
652
|
with monkeypatch.context() as m:
|
640
653
|
m.setattr("asyncio.sleep", fake_sleep)
|
641
|
-
await cli.login(vo="diracAdmin", group=None, property=None)
|
654
|
+
await cli.auth.login(vo="diracAdmin", group=None, property=None)
|
642
655
|
captured = capfd.readouterr()
|
643
656
|
assert "Login successful!" in captured.out
|
644
657
|
assert captured.err == ""
|
@@ -689,63 +702,3 @@ def do_device_flow_with_dex(url: str, ca_path: str) -> None:
|
|
689
702
|
|
690
703
|
# This should have redirected to the DiracX page that shows the login is complete
|
691
704
|
assert "Please close the window" in r.text
|
692
|
-
|
693
|
-
|
694
|
-
def get_installed_entry_points():
|
695
|
-
"""Retrieve the installed entry points from the environment."""
|
696
|
-
entry_pts = entry_points()
|
697
|
-
diracx_eps = defaultdict(dict)
|
698
|
-
for group in entry_pts.groups:
|
699
|
-
if "diracx" in group:
|
700
|
-
for ep in entry_pts.select(group=group):
|
701
|
-
diracx_eps[group][ep.name] = ep.value
|
702
|
-
return dict(diracx_eps)
|
703
|
-
|
704
|
-
|
705
|
-
def get_entry_points_from_toml(toml_file):
|
706
|
-
"""Parse entry points from pyproject.toml."""
|
707
|
-
with open(toml_file, "rb") as f:
|
708
|
-
pyproject = tomllib.load(f)
|
709
|
-
package_name = pyproject["project"]["name"]
|
710
|
-
return package_name, pyproject.get("project", {}).get("entry-points", {})
|
711
|
-
|
712
|
-
|
713
|
-
def get_current_entry_points(repo_base) -> bool:
|
714
|
-
"""Create current entry points dict for comparison."""
|
715
|
-
current_eps = {}
|
716
|
-
for toml_file in repo_base.glob("diracx-*/pyproject.toml"):
|
717
|
-
package_name, entry_pts = get_entry_points_from_toml(f"{toml_file}")
|
718
|
-
# Ignore packages that are not installed
|
719
|
-
try:
|
720
|
-
distribution(package_name)
|
721
|
-
except PackageNotFoundError:
|
722
|
-
continue
|
723
|
-
# Merge the entry points
|
724
|
-
for key, value in entry_pts.items():
|
725
|
-
current_eps[key] = current_eps.get(key, {}) | value
|
726
|
-
return current_eps
|
727
|
-
|
728
|
-
|
729
|
-
@pytest.fixture(scope="session", autouse=True)
|
730
|
-
def verify_entry_points(request, pytestconfig):
|
731
|
-
try:
|
732
|
-
ini_toml_name = tomllib.loads(pytestconfig.inipath.read_text())["project"][
|
733
|
-
"name"
|
734
|
-
]
|
735
|
-
except tomllib.TOMLDecodeError:
|
736
|
-
return
|
737
|
-
if ini_toml_name == "diracx":
|
738
|
-
repo_base = pytestconfig.inipath.parent
|
739
|
-
elif ini_toml_name.startswith("diracx-"):
|
740
|
-
repo_base = pytestconfig.inipath.parent.parent
|
741
|
-
else:
|
742
|
-
return
|
743
|
-
|
744
|
-
installed_eps = get_installed_entry_points()
|
745
|
-
current_eps = get_current_entry_points(repo_base)
|
746
|
-
|
747
|
-
if installed_eps != current_eps:
|
748
|
-
pytest.fail(
|
749
|
-
"Project and installed entry-points are not consistent. "
|
750
|
-
"You should run `pip install -r requirements-dev.txt`",
|
751
|
-
)
|
@@ -2,9 +2,11 @@ README.md
|
|
2
2
|
pyproject.toml
|
3
3
|
src/diracx/testing/__init__.py
|
4
4
|
src/diracx/testing/dummy_osdb.py
|
5
|
+
src/diracx/testing/entrypoints.py
|
5
6
|
src/diracx/testing/mock_osdb.py
|
6
7
|
src/diracx/testing/osdb.py
|
7
8
|
src/diracx/testing/routers.py
|
9
|
+
src/diracx/testing/utils.py
|
8
10
|
src/diracx_testing.egg-info/PKG-INFO
|
9
11
|
src/diracx_testing.egg-info/SOURCES.txt
|
10
12
|
src/diracx_testing.egg-info/dependency_links.txt
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{diracx_testing-0.0.1a23 → diracx_testing-0.0.1a25}/src/diracx_testing.egg-info/dependency_links.txt
RENAMED
File without changes
|
{diracx_testing-0.0.1a23 → diracx_testing-0.0.1a25}/src/diracx_testing.egg-info/requires.txt
RENAMED
File without changes
|
{diracx_testing-0.0.1a23 → diracx_testing-0.0.1a25}/src/diracx_testing.egg-info/top_level.txt
RENAMED
File without changes
|