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.
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: diracx-testing
3
- Version: 0.0.1a23
3
+ Version: 0.0.1a25
4
4
  Summary: TODO
5
5
  License: GPL-3.0-only
6
6
  Classifier: Intended Audience :: Science/Research
@@ -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
- MockedDB = type(f"Mocked{self.__class__.__name__}", (sql_utils.BaseSQLDB,), {})
46
- self._sql_db = MockedDB(connection_kwargs["sqlalchemy_dsn"])
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
- ColumnType = DateNowColumn
56
+ column_type = DateNowColumn
57
57
  case "long":
58
- ColumnType = partial(Column, type_=Integer)
58
+ column_type = partial(Column, type_=Integer)
59
59
  case "keyword":
60
- ColumnType = partial(Column, type_=String(255))
60
+ column_type = partial(Column, type_=String(255))
61
61
  case "text":
62
- ColumnType = partial(Column, type_=String(64 * 1024))
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(ColumnType(field, default=None))
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
- await self._sql_db.__aenter__()
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
- await self._sql_db.__aexit__(exc_type, exc_value, traceback)
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
- return await self._sql_db.ping()
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
- MockParameterDB = type(name, (MockOSDBMixin, implementations[0]), {})
166
+ mock_parameter_db = type(name, (MockOSDBMixin, implementations[0]), {})
162
167
 
163
- return [MockParameterDB] + implementations
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(private_key_pem, fernet_key) -> AuthSettings:
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
- isinstance(v, UnavailableDependency)
290
- or k.__func__ != BaseSQLDB.transaction.__func__
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
- )
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: diracx-testing
3
- Version: 0.0.1a23
3
+ Version: 0.0.1a25
4
4
  Summary: TODO
5
5
  License: GPL-3.0-only
6
6
  Classifier: Intended Audience :: Science/Research
@@ -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