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.
@@ -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