diracx-testing 0.0.1a43__py3-none-any.whl → 0.0.1a45__py3-none-any.whl

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.
@@ -11,9 +11,8 @@ from .utils import (
11
11
  demo_urls,
12
12
  do_device_flow_with_dex,
13
13
  fernet_key,
14
- private_key_pem,
14
+ private_key,
15
15
  pytest_addoption,
16
- pytest_collection_modifyitems,
17
16
  session_client_factory,
18
17
  test_auth_settings,
19
18
  test_dev_settings,
@@ -29,8 +28,7 @@ __all__ = (
29
28
  "do_device_flow_with_dex",
30
29
  "test_login",
31
30
  "pytest_addoption",
32
- "pytest_collection_modifyitems",
33
- "private_key_pem",
31
+ "private_key",
34
32
  "fernet_key",
35
33
  "test_dev_settings",
36
34
  "test_auth_settings",
@@ -0,0 +1,67 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import shlex
5
+ import subprocess
6
+ from importlib.resources import as_file, files
7
+ from pathlib import Path
8
+ from typing import NoReturn
9
+
10
+ SCRIPTS_BASE = files("diracx.testing").joinpath("scripts")
11
+
12
+
13
+ def parse_args() -> None:
14
+ """Access to various utility scripts for testing DiracX and extensions."""
15
+ parser = argparse.ArgumentParser(description="Utility for testing DiracX.")
16
+ sp = parser.add_subparsers(dest="command", required=True)
17
+
18
+ # Create the 'coverage' argument group.
19
+ coverage_p = sp.add_parser("coverage", help="Coverage related commands")
20
+ coverage_sp = coverage_p.add_subparsers(dest="subcommand", required=True)
21
+
22
+ # Add the 'collect-demo' command under 'coverage'.
23
+ collect_demo_p = coverage_sp.add_parser(
24
+ "collect-demo", help="Collect demo coverage"
25
+ )
26
+ collect_demo_p.add_argument(
27
+ "--demo-dir",
28
+ required=True,
29
+ type=Path,
30
+ help="Path to the .demo dir of the diracx-charts repo.",
31
+ )
32
+ collect_demo_p.set_defaults(func=lambda a: coverage_collect_demo(a.demo_dir))
33
+
34
+ args = parser.parse_args()
35
+ args.func(args)
36
+
37
+
38
+ def coverage_collect_demo(demo_dir: Path) -> NoReturn:
39
+ """Collect coverage data from a running instance of the demo.
40
+
41
+ This script is primarily intended for use in CI/CD pipelines.
42
+ """
43
+ from diracx.core.extensions import extensions_by_priority
44
+
45
+ client_extension_name = min(extensions_by_priority(), key=lambda x: x == "diracx")
46
+
47
+ with as_file(SCRIPTS_BASE / "collect_demo_coverage.sh") as script_file:
48
+ cmd = ["bash", str(script_file), "--demo-dir", str(demo_dir)]
49
+ if client_extension_name in {"diracx", "gubbins"}:
50
+ cmd += ["--diracx-repo", str(Path.cwd())]
51
+ if client_extension_name == "gubbins":
52
+ cmd += ["--extension-name", "gubbins"]
53
+ cmd += ["--extension-repo", str(Path.cwd() / "extensions" / "gubbins")]
54
+ else:
55
+ cmd += ["--extension-name", client_extension_name]
56
+ cmd += ["--extension-repo", str(Path.cwd())]
57
+ print("Running:", shlex.join(cmd))
58
+ proc = subprocess.run(cmd, check=False)
59
+ if proc.returncode == 0:
60
+ print("Process completed successfully.")
61
+ else:
62
+ print("Process failed with return code", proc.returncode)
63
+ raise SystemExit(proc.returncode)
64
+
65
+
66
+ if __name__ == "__main__":
67
+ parse_args()
@@ -2,17 +2,27 @@ from __future__ import annotations
2
2
 
3
3
  __all__ = [
4
4
  "regenerate_client",
5
- "AUTOREST_VERSION",
6
5
  ]
7
6
 
7
+ import argparse
8
8
  import ast
9
9
  import importlib.util
10
+ import json
11
+ import shlex
10
12
  import subprocess
13
+ import sys
14
+ from importlib.metadata import Distribution
11
15
  from pathlib import Path
16
+ from urllib.parse import urlparse
12
17
 
13
18
  import git
14
19
 
15
- AUTOREST_VERSION = "6.13.7"
20
+ AUTOREST_VERSION = "3.7.1"
21
+ AUTOREST_CORE_VERSION = "3.10.4"
22
+ AUTOREST_PUGINS = {
23
+ "@autorest/python": "6.34.2",
24
+ "@autorest/modelerfour": "4.23.7",
25
+ }
16
26
 
17
27
 
18
28
  def extract_static_all(path):
@@ -70,6 +80,26 @@ def fixup_models_init(generated_dir, extension_name):
70
80
  )
71
81
 
72
82
 
83
+ def _module_path(module_name: str) -> Path:
84
+ """Get the path to a module.
85
+
86
+ Args:
87
+ module_name: The name of the module.
88
+
89
+ Returns:
90
+ The path to the module.
91
+
92
+ """
93
+ spec = importlib.util.find_spec(module_name)
94
+ if spec is None:
95
+ raise ImportError("Cannot locate client_module package")
96
+ if spec.origin is None:
97
+ raise ImportError(
98
+ "Cannot locate client_module package, did you forget the __init__.py?"
99
+ )
100
+ return Path(spec.origin).parent
101
+
102
+
73
103
  def regenerate_client(openapi_spec: Path, client_module: str):
74
104
  """Regenerate the AutoREST client and run pre-commit checks on it.
75
105
 
@@ -82,14 +112,29 @@ def regenerate_client(openapi_spec: Path, client_module: str):
82
112
 
83
113
  WARNING: This test will modify the source code of the client!
84
114
  """
85
- spec = importlib.util.find_spec(client_module)
86
- if spec is None:
87
- raise ImportError("Cannot locate client_module package")
88
- if spec.origin is None:
89
- raise ImportError(
90
- "Cannot locate client_module package, did you forget the __init__.py?"
115
+ client_root = _module_path(client_module)
116
+
117
+ # If the client is not an editable install, we need to find the source code
118
+ direct_url = Distribution.from_name(client_module).read_text("direct_url.json")
119
+ pkg_url_info = json.loads(direct_url)
120
+ if not pkg_url_info.get("dir_info", {}).get("editable", False):
121
+ src_url = pkg_url_info.get("url")
122
+ if src_url is None:
123
+ raise ValueError("No URL found in direct_url.json")
124
+ url_info = urlparse(src_url)
125
+ if url_info.scheme != "file":
126
+ raise ValueError("URL is not a file URL")
127
+ pkg_root = Path(url_info.path) / "src" / client_module.replace(".", "/")
128
+ if not pkg_root.is_dir():
129
+ raise NotImplementedError(
130
+ "Failed to resolve client sources", client_module, pkg_root
131
+ )
132
+ client_root = pkg_root
133
+
134
+ if not pkg_url_info.get("dir_info", {}).get("editable", False):
135
+ raise NotImplementedError(
136
+ "Client generation from non-editable install is not yet supported",
91
137
  )
92
- client_root = Path(spec.origin).parent
93
138
 
94
139
  assert client_root.is_dir()
95
140
  assert client_root.name == "client"
@@ -97,7 +142,7 @@ def regenerate_client(openapi_spec: Path, client_module: str):
97
142
  extension_name = client_root.parent.name
98
143
 
99
144
  repo_root = client_root.parents[3]
100
- if extension_name == "gubbins":
145
+ if extension_name == "gubbins" and not (repo_root / ".git").is_dir():
101
146
  # Gubbins is special because it has a different structure due to being
102
147
  # in a subdirectory of diracx
103
148
  repo_root = repo_root.parents[1]
@@ -109,8 +154,10 @@ def regenerate_client(openapi_spec: Path, client_module: str):
109
154
  "Client is currently in a modified state, skipping regeneration"
110
155
  )
111
156
 
112
- cmd = [
113
- "autorest",
157
+ cmd = ["autorest", f"--version={AUTOREST_CORE_VERSION}"]
158
+ for plugin, version in AUTOREST_PUGINS.items():
159
+ cmd.append(f"--use={plugin}@{version}")
160
+ cmd += [
114
161
  "--python",
115
162
  f"--input-file={openapi_spec}",
116
163
  "--models-mode=msrest",
@@ -118,11 +165,6 @@ def regenerate_client(openapi_spec: Path, client_module: str):
118
165
  f"--output-folder={client_root}",
119
166
  ]
120
167
 
121
- # This is required to be able to work offline
122
- # TODO: if offline, find the version already installed
123
- # and use it
124
- # cmd += [f"--use=@autorest/python@{AUTOREST_VERSION}"]
125
-
126
168
  # ruff: disable=S603
127
169
  subprocess.run(cmd, check=True)
128
170
 
@@ -154,3 +196,36 @@ def regenerate_client(openapi_spec: Path, client_module: str):
154
196
  if proc.returncode != 0:
155
197
  raise AssertionError("Pre-commit failed")
156
198
  raise AssertionError("Client was regenerated with changes")
199
+
200
+
201
+ def main():
202
+ from diracx.core.extensions import extensions_by_priority
203
+
204
+ parser = argparse.ArgumentParser(
205
+ description="Regenerate the AutoREST client and run pre-commit checks on it."
206
+ )
207
+ parser.parse_args()
208
+
209
+ client_extension_name = min(extensions_by_priority(), key=lambda x: x == "diracx")
210
+
211
+ cmd = ["npm", "install", "-g", f"autorest@{AUTOREST_VERSION}"]
212
+ print("Ensuring autorest is installed by running", shlex.join(cmd))
213
+ subprocess.run(cmd, check=True)
214
+
215
+ cmd = [
216
+ sys.executable,
217
+ "-m",
218
+ "pytest",
219
+ "-pdiracx.testing",
220
+ "--import-mode=importlib",
221
+ "--no-cov",
222
+ f"--regenerate-client={client_extension_name}",
223
+ str(Path(__file__).parent / "client_generation_pytest.py"),
224
+ ]
225
+ print("Generating client for", client_extension_name)
226
+ print("Running:", shlex.join(cmd))
227
+ subprocess.run(cmd, check=True)
228
+
229
+
230
+ if __name__ == "__main__":
231
+ main()
@@ -0,0 +1,37 @@
1
+ from __future__ import annotations
2
+
3
+ import pytest
4
+
5
+ from diracx.testing.client_generation import regenerate_client
6
+
7
+ pytestmark = pytest.mark.enabled_dependencies([])
8
+
9
+
10
+ @pytest.fixture
11
+ def test_client(client_factory):
12
+ with client_factory.unauthenticated() as client:
13
+ yield client
14
+
15
+
16
+ def test_regenerate_client(test_client, tmp_path, request):
17
+ """Regenerate the AutoREST client and run pre-commit checks on it.
18
+
19
+ This test is skipped by default, and can be enabled by passing
20
+ --regenerate-client to pytest. It is intended to be run manually
21
+ when the API changes.
22
+
23
+ The reason this is a test is that it is the only way to get access to the
24
+ test_client fixture, which is required to get the OpenAPI spec.
25
+
26
+ WARNING: This test will modify the source code of the client!
27
+ """
28
+ client_name = request.config.getoption("--regenerate-client")
29
+ if client_name is None:
30
+ pytest.skip("--regenerate-client not specified")
31
+
32
+ r = test_client.get("/api/openapi.json")
33
+ r.raise_for_status()
34
+ openapi_spec = tmp_path / "openapi.json"
35
+ openapi_spec.write_text(r.text)
36
+
37
+ regenerate_client(openapi_spec, f"{client_name}.client")
@@ -0,0 +1,147 @@
1
+ #!/usr/bin/env bash
2
+ # Enable the unofficial bash strict mode
3
+ # See: http://redsymbol.net/articles/unofficial-bash-strict-mode/
4
+ set -euo pipefail
5
+ IFS=$'\n\t'
6
+
7
+ usage() {
8
+ echo "Usage: $(basename "$0") [OPTIONS]"
9
+ echo "Collect coverage data from a demo run."
10
+ echo
11
+ echo "Options:"
12
+ echo " --demo-dir <path> Path to the demo directory."
13
+ echo " --diracx-repo <path> Path to the DiracX repository."
14
+ echo " --extension-name <name> Name of the extension to collect coverage for."
15
+ echo " --extension-repo <path> Path to the extension repository."
16
+ echo " -h, --help Show this help message and exit."
17
+ echo
18
+ exit 1
19
+ }
20
+
21
+ # Parse command line arguments
22
+ demo_dir=""
23
+ diracx_repo=""
24
+ extension_name=""
25
+ extension_repo=""
26
+
27
+ while [[ "$#" -gt 0 ]]; do
28
+ case "$1" in
29
+ --demo-dir)
30
+ if [[ -n "${2:-}" ]]; then
31
+ demo_dir="$2"
32
+ shift 2
33
+ else
34
+ echo "Error: --demo-dir requires a value."
35
+ usage
36
+ fi
37
+ ;;
38
+ --diracx-repo)
39
+ if [[ -n "${2:-}" ]]; then
40
+ diracx_repo=$(realpath "$2")
41
+ shift 2
42
+ else
43
+ echo "Error: --diracx-repo requires a value."
44
+ usage
45
+ fi
46
+ ;;
47
+ --extension-name)
48
+ if [[ -n "${2:-}" ]]; then
49
+ extension_name="$2"
50
+ shift 2
51
+ else
52
+ echo "Error: --extension-name requires a value."
53
+ usage
54
+ fi
55
+ ;;
56
+ --extension-repo)
57
+ if [[ -n "${2:-}" ]]; then
58
+ extension_repo=$(realpath "$2")
59
+ shift 2
60
+ else
61
+ echo "Error: --extension-repo requires a value."
62
+ usage
63
+ fi
64
+ ;;
65
+ *)
66
+ echo "Unknown parameter passed: $1"
67
+ usage
68
+ ;;
69
+ esac
70
+ done
71
+
72
+ # Argument validation
73
+ if [[ -z "$demo_dir" ]]; then
74
+ echo "Error: --demo-dir is required."
75
+ usage
76
+ fi
77
+ if [[ -z "${diracx_repo:-}" && -z "${extension_repo:-}" ]]; then
78
+ echo "Error: At least one of --diracx-repo or --extension-repo is required."
79
+ usage
80
+ fi
81
+ if [[ -n "${extension_name:-}" && -z "${extension_repo:-}" ]]; then
82
+ echo "Error: --extension-name is required when --extension-repo is provided."
83
+ usage
84
+ fi
85
+ if [[ ! -f ".coveragerc" ]]; then
86
+ echo "Error: Expected .coveragerc file not found in the current directory."
87
+ exit 1
88
+ fi
89
+
90
+ # Set up the environment
91
+ export KUBECONFIG=${demo_dir}/kube.conf
92
+ export PATH=${demo_dir}:$PATH
93
+
94
+ if ! kubectl cluster-info > /dev/null 2>&1; then
95
+ echo "Error: The demo does not appear to be running."
96
+ exit 1
97
+ fi
98
+
99
+ # Shutdown the pods so we collect coverage data
100
+ pods=$(kubectl get pods -o json | jq -r '.items[] | .metadata.name')
101
+ for pod_name in $(echo "${pods}" | grep -vE '(dex|minio|mysql|rabbitmq|opensearch)'); do
102
+ kubectl delete pod/"${pod_name}"
103
+ done
104
+
105
+ # Combine the coverage data from the demo and make an XML report
106
+ coverage_data=$(mktemp)
107
+ echo "Changing ownership of coverage data to $(id -u)"
108
+ sudo chown -R "$(id -u)" "${demo_dir}"/coverage-reports/
109
+ coverage combine --keep --data-file "${coverage_data}" "${demo_dir}"/coverage-reports/*
110
+
111
+ # coverage can't handle having multiple src directories, so we need to make a fake one with symlinks
112
+ fake_module=$(mktemp -d)
113
+
114
+ # Ensure we clean up the fake module and restore the original .coveragerc
115
+ cleanup() {
116
+ rm -rf "${fake_module}"
117
+ if [[ -f ".coveragerc.bak" ]]; then
118
+ mv ".coveragerc.bak" .coveragerc
119
+ fi
120
+ }
121
+ trap cleanup EXIT
122
+
123
+ # Symlink DiracX into the fake module (if provided)
124
+ if [[ -n "${diracx_repo:-}" ]]; then
125
+ mkdir -p "${fake_module}/src/diracx"
126
+ for fn in "${diracx_repo}"/*/src/diracx/*; do
127
+ ln -sf "${fn}" "${fake_module}/src/diracx/$(basename "${fn}")"
128
+ done
129
+ fi
130
+
131
+ # Symlink the extension into the fake module (if provided)
132
+ if [[ -n "${extension_repo:-}" ]]; then
133
+ mkdir -p "${fake_module}/src/${extension_name}/extensions"
134
+ for fn in "${extension_repo}"/*/src/"${extension_name}"/*; do
135
+ ln -sf "${fn}" "${fake_module}/src/${extension_name}/$(basename "${fn}")"
136
+ done
137
+ fi
138
+
139
+ # Edit the .coveragerc file to point to the fake module
140
+ sed -i.bak "s@source =@source =\n ${fake_module}/src@g" .coveragerc
141
+ if diff -q .coveragerc .coveragerc.bak > /dev/null; then
142
+ echo "Error: .coveragerc was not modified."
143
+ exit 1
144
+ fi
145
+
146
+ # Make the actual coverage report
147
+ coverage xml -o coverage-demo.xml --data-file "${coverage_data}"
diracx/testing/time.py ADDED
@@ -0,0 +1,135 @@
1
+ """Helper functions for mocking time functions.
2
+
3
+ The main functionality in this module is to mock SQLite's date and time
4
+ functions. This is useful for testing purposes, especially when using
5
+ freezegun to freeze time.
6
+
7
+ Example usage to mock SQLite time functions in SQLAlchemy for connections in
8
+ a given engine:
9
+
10
+ import sqlalchemy
11
+
12
+ from diracx.testing.time import mock_sqlite_time
13
+
14
+ sqlalchemy.event.listen(engine.sync_engine, "connect", mock_sqlite_time)
15
+
16
+ This functionality is tested in the `diracx-db` tests, specifically in the
17
+ `test_freeze_time.py` file.
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ __all__ = [
23
+ "mock_sqlite_time",
24
+ "julian_date",
25
+ ]
26
+
27
+ import re
28
+ from datetime import UTC, datetime
29
+
30
+ RE_SQLITE_TIME = re.compile(r"(\d{4})-(\d{2})-(\d{2})(?: (\d{2}):(\d{2}):(\d{2}))?")
31
+
32
+
33
+ def mock_sqlite_time(dbapi_connection, connection_record):
34
+ """Override SQLite’s date and time functions.
35
+
36
+ See: https://sqlite.org/lang_datefunc.html#tmval
37
+ """
38
+
39
+ # 1. date(time-value, modifier, modifier, ...)
40
+ def date_mock(*args):
41
+ """Mock for DATE() function."""
42
+ raise NotImplementedError(args)
43
+
44
+ # 2. time(time-value, modifier, modifier, ...)
45
+ def time_mock(*args):
46
+ """Mock for TIME() function."""
47
+ raise NotImplementedError(args)
48
+
49
+ # 3. datetime(time-value, modifier, modifier, ...)
50
+ def datetime_mock(*args):
51
+ """Mock for DATETIME() function."""
52
+ if len(args) == 0:
53
+ args = ("now",)
54
+ if len(args) > 1:
55
+ raise NotImplementedError(args)
56
+
57
+ time_value = args[0]
58
+ if time_value == "now":
59
+ return datetime.now(UTC).strftime("%Y-%m-%d %H:%M:%S")
60
+
61
+ raise NotImplementedError(args)
62
+
63
+ # 7. julianday(time-value, modifier, modifier, ...)
64
+ def julianday_mock(*args):
65
+ """Mock for JULIANDAY() function."""
66
+ if len(args) == 0:
67
+ args = ("now",)
68
+ if len(args) > 1:
69
+ raise NotImplementedError(args)
70
+
71
+ if args[0] == "now":
72
+ return julian_date(datetime.now(UTC))
73
+ if match := RE_SQLITE_TIME.fullmatch(args[0]):
74
+ parts = [0 if x is None else int(x) for x in match.groups()]
75
+ return julian_date_from_parts(*parts)
76
+ if match := re.fullmatch(r"(\d{4})(?:\.(\d{2}))?", args[0]):
77
+ return float(args[0])
78
+
79
+ raise NotImplementedError(args[0])
80
+
81
+ # 5. unixepoch(time-value, modifier, modifier, ...)
82
+ def unixepoch_mock(*args):
83
+ """Mock for UNIXEPOCH() function."""
84
+ raise NotImplementedError(args)
85
+
86
+ # 6. strftime(format, time-value, modifier, modifier, ...)
87
+ def strftime_mock(*args):
88
+ """Mock for STRFTIME() function."""
89
+ raise NotImplementedError(args)
90
+
91
+ # 7. timediff(time-value, time-value)
92
+ def timediff_mock(*args):
93
+ """Mock for TIMEDIFF() function."""
94
+ raise NotImplementedError(args)
95
+
96
+ conn = dbapi_connection
97
+ conn.create_function("DATE", -1, date_mock)
98
+ conn.create_function("TIME", -1, time_mock)
99
+ conn.create_function("DATETIME", -1, datetime_mock)
100
+ conn.create_function("JULIANDAY", -1, julianday_mock)
101
+ conn.create_function("UNIXEPOCH", -1, unixepoch_mock)
102
+ conn.create_function("STRFTIME", -1, strftime_mock)
103
+ conn.create_function("TIMEDIFF", -1, timediff_mock)
104
+
105
+
106
+ def julian_date(dt: datetime) -> float:
107
+ """Convert a datetime to Julian Date."""
108
+ if dt.tzinfo is None:
109
+ raise ValueError("Datetime must be timezone-aware")
110
+ if dt.tzinfo.utcoffset(dt) is None:
111
+ raise ValueError("Datetime must be timezone-aware")
112
+ dt = dt.astimezone(UTC)
113
+
114
+ return julian_date_from_parts(
115
+ dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second
116
+ )
117
+
118
+
119
+ def julian_date_from_parts(
120
+ year: int, month: int, day: int, hour: int, minute: int, second: int
121
+ ) -> float:
122
+ """Convert year, month, day, hour, minute, second to Julian Date.
123
+
124
+ The time MUST be in UTC for the conversion to be correct.
125
+ """
126
+ # Step 1: break out the "month shift"
127
+ a = (14 - month) // 12
128
+ y = year + 4800 - a
129
+ m = month + 12 * a - 3
130
+
131
+ # Step 2: compute the (integer) Julian Day Number
132
+ jdn = day + (153 * m + 2) // 5 + 365 * y + y // 4 - y // 100 + y // 400 - 32045
133
+
134
+ # Step 3: add fractional day
135
+ return jdn + (hour - 12) / 24 + minute / 1440 + second / 86400
diracx/testing/utils.py CHANGED
@@ -6,6 +6,7 @@ from __future__ import annotations
6
6
  # are the enabled_dependencies markers
7
7
  import asyncio
8
8
  import contextlib
9
+ import json
9
10
  import os
10
11
  import re
11
12
  import ssl
@@ -19,6 +20,7 @@ from urllib.parse import parse_qs, urljoin, urlparse
19
20
 
20
21
  import httpx
21
22
  import pytest
23
+ from joserfc.jwk import KeySet, OKPKey
22
24
  from uuid_utils import uuid7
23
25
 
24
26
  from diracx.core.models import AccessTokenPayload, RefreshTokenPayload
@@ -43,8 +45,6 @@ ACCESS_TOKEN_EXPIRE_MINUTES = 30
43
45
  def pytest_addoption(parser):
44
46
  parser.addoption(
45
47
  "--regenerate-client",
46
- action="store_true",
47
- default=False,
48
48
  help="Regenerate the AutoREST client",
49
49
  )
50
50
  parser.addoption(
@@ -55,28 +55,15 @@ def pytest_addoption(parser):
55
55
  )
56
56
 
57
57
 
58
- def pytest_collection_modifyitems(config, items):
59
- """Disable the test_regenerate_client if not explicitly asked for."""
60
- if config.getoption("--regenerate-client"):
61
- # --regenerate-client given in cli: allow client re-generation
62
- return
63
- skip_regen = pytest.mark.skip(reason="need --regenerate-client option to run")
64
- for item in items:
65
- if item.name == "test_regenerate_client":
66
- item.add_marker(skip_regen)
67
-
68
-
69
58
  @pytest.fixture(scope="session")
70
- def private_key_pem() -> str:
71
- from cryptography.hazmat.primitives import serialization
72
- from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
73
-
74
- private_key = Ed25519PrivateKey.generate()
75
- return private_key.private_bytes(
76
- encoding=serialization.Encoding.PEM,
77
- format=serialization.PrivateFormat.PKCS8,
78
- encryption_algorithm=serialization.NoEncryption(),
79
- ).decode()
59
+ def private_key() -> OKPKey:
60
+ return OKPKey.generate_key(
61
+ parameters={
62
+ "key_ops": ["sign", "verify"],
63
+ "alg": "EdDSA",
64
+ "kid": uuid7().hex,
65
+ }
66
+ )
80
67
 
81
68
 
82
69
  @pytest.fixture(scope="session")
@@ -94,15 +81,12 @@ def test_dev_settings() -> Generator[DevelopmentSettings, None, None]:
94
81
 
95
82
 
96
83
  @pytest.fixture(scope="session")
97
- def test_auth_settings(
98
- private_key_pem, fernet_key
99
- ) -> Generator[AuthSettings, None, None]:
84
+ def test_auth_settings(private_key, fernet_key) -> Generator[AuthSettings, None, None]:
100
85
  from diracx.core.settings import AuthSettings
101
86
 
102
87
  yield AuthSettings(
103
88
  token_issuer=ISSUER,
104
- token_algorithm="EdDSA",
105
- token_key=private_key_pem,
89
+ token_keystore=json.dumps(KeySet([private_key]).as_dict(private_keys=True)),
106
90
  state_key=fernet_key,
107
91
  allowed_redirects=[
108
92
  "http://diracx.test.invalid:8000/api/docs/oauth2-redirect",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: diracx-testing
3
- Version: 0.0.1a43
3
+ Version: 0.0.1a45
4
4
  Summary: TODO
5
5
  License: GPL-3.0-only
6
6
  Classifier: Intended Audience :: Science/Research
@@ -9,12 +9,11 @@ Classifier: Programming Language :: Python :: 3
9
9
  Classifier: Topic :: Scientific/Engineering
10
10
  Classifier: Topic :: System :: Distributed Computing
11
11
  Requires-Python: >=3.11
12
- Description-Content-Type: text/markdown
12
+ Requires-Dist: httpx
13
+ Requires-Dist: joserfc
13
14
  Requires-Dist: pytest
14
15
  Requires-Dist: pytest-asyncio
15
16
  Requires-Dist: pytest-cov
16
17
  Requires-Dist: pytest-xdist
17
- Requires-Dist: httpx
18
18
  Requires-Dist: uuid-utils
19
19
  Provides-Extra: testing
20
- Requires-Dist: diracx-testing; extra == "testing"
@@ -0,0 +1,15 @@
1
+ diracx/testing/__init__.py,sha256=ocW2U2SaWC9zvTjWMVhOQtFQ4rqSJbsCJXCMUpBj0rs,904
2
+ diracx/testing/__main__.py,sha256=cd6HxeVKqVrxk32uAB_4O0PzzAo_rzwEK2GN7kND8XY,2419
3
+ diracx/testing/client_generation.py,sha256=B1_b1ku2GEJkDqfDrro3ZpwMbzE5Pxo7fkZ3ew0VYe4,7949
4
+ diracx/testing/client_generation_pytest.py,sha256=c4lvwi6U76yu_iNT6N3zgrdVsBhMPNZi50CXVXYNsi4,1173
5
+ diracx/testing/dummy_osdb.py,sha256=ptLrrreOHJBKMV7S1pR_k83jl-aLKvw7bFZufB6570Y,916
6
+ diracx/testing/entrypoints.py,sha256=ql7byyUOnUBdDoiN5xznlUTQSbD1YCNy4UF5t9MBhgc,2330
7
+ diracx/testing/mock_osdb.py,sha256=2pl9yKV1-e5kzf6meBZnhoc1i0ySAJ8IdnP3r1bOwcI,6033
8
+ diracx/testing/osdb.py,sha256=m6mUBLnGOoQLTCIBie9P2GhmLMybrgzIrlIYfhF1_Ss,3230
9
+ diracx/testing/routers.py,sha256=UW-TnikMQgcNxF5sUZD5DWoucGiCpP6s8mYmuahDiSc,979
10
+ diracx/testing/time.py,sha256=ec2b4yn-52ey8ea1oINdknxtQf-CpPRBIiI15Wa1W2Q,4320
11
+ diracx/testing/utils.py,sha256=c8NA1RMPemAUfrec8YfphcsKsauWlIJ1vyOL3-uWjuM,23804
12
+ diracx/testing/scripts/collect_demo_coverage.sh,sha256=EyIuP6rA-g542BHTPL2cD4qrBSm-FQ5y1e0c1qhVcEs,4555
13
+ diracx_testing-0.0.1a45.dist-info/METADATA,sha256=FHxCST0jbSX2EM6oNwELxd3Fh1YnP3u3LINJC9jM6f4,593
14
+ diracx_testing-0.0.1a45.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
15
+ diracx_testing-0.0.1a45.dist-info/RECORD,,
@@ -1,5 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (78.1.0)
2
+ Generator: hatchling 1.27.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
-
@@ -1,12 +0,0 @@
1
- diracx/testing/__init__.py,sha256=nGbnGP8m53N9rqHR-hyqDa5vetcsmnQp806HadV6_dE,984
2
- diracx/testing/client_generation.py,sha256=vegOUVQPcB7NTUFDr0HbasH5eByKQvLh_xYqKBYajzk,5539
3
- diracx/testing/dummy_osdb.py,sha256=ptLrrreOHJBKMV7S1pR_k83jl-aLKvw7bFZufB6570Y,916
4
- diracx/testing/entrypoints.py,sha256=ql7byyUOnUBdDoiN5xznlUTQSbD1YCNy4UF5t9MBhgc,2330
5
- diracx/testing/mock_osdb.py,sha256=2pl9yKV1-e5kzf6meBZnhoc1i0ySAJ8IdnP3r1bOwcI,6033
6
- diracx/testing/osdb.py,sha256=m6mUBLnGOoQLTCIBie9P2GhmLMybrgzIrlIYfhF1_Ss,3230
7
- diracx/testing/routers.py,sha256=UW-TnikMQgcNxF5sUZD5DWoucGiCpP6s8mYmuahDiSc,979
8
- diracx/testing/utils.py,sha256=MnFUgRWfpU9GFOvP5d-LQS0uo1grHy_-iNOGLS7XlNk,24483
9
- diracx_testing-0.0.1a43.dist-info/METADATA,sha256=4UiegQpc2BUW4l9GLXFpshvsbTWVOpTNbs4njc6cJDA,660
10
- diracx_testing-0.0.1a43.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
11
- diracx_testing-0.0.1a43.dist-info/top_level.txt,sha256=vJx10tdRlBX3rF2Psgk5jlwVGZNcL3m_7iQWwgPXt-U,7
12
- diracx_testing-0.0.1a43.dist-info/RECORD,,
@@ -1 +0,0 @@
1
- diracx