diracx-testing 0.0.1a23__tar.gz → 0.0.1a25__tar.gz
Sign up to get free protection for your applications and to get access to all the features.
- {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
|