authentik.root.test_plugin
1import math 2from os import environ 3from ssl import OPENSSL_VERSION 4from time import monotonic 5from typing import TextIO 6 7import pytest 8from cryptography.hazmat.backends.openssl.backend import backend 9from pytest import Config, Item, TerminalReporter 10 11from authentik import authentik_full_version 12from tests.decorators import get_local_ip 13 14IS_CI = "CI" in environ 15 16 17@pytest.hookimpl(hookwrapper=True) 18def pytest_sessionstart(*_, **__): 19 """Clear the console ahead of the pytest output starting""" 20 if not IS_CI: 21 print("\x1b[2J\x1b[H") 22 # Pre-warm cryptography's PyO3 PyDateTime type cache with the real 23 # datetime class. If the first extraction happens under @freeze_time 24 # (e.g. in MTLSStageTests), PyO3 caches freezegun's FakeDatetime, 25 # which breaks every later test that passes a real datetime into 26 # cryptography ("TypeError: 'datetime' object is not an instance 27 # of 'FakeDatetime'"). The discard is intentional — only side 28 # effect needed is the type-cache initialization. 29 from datetime import UTC, datetime 30 31 from cryptography.x509.verification import PolicyBuilder 32 33 PolicyBuilder().time(datetime.now(tz=UTC)) 34 yield 35 36 37@pytest.hookimpl(trylast=True) 38def pytest_report_header(*_, **__): 39 """Add authentik version to pytest output""" 40 return [ 41 f"authentik version: {authentik_full_version()}", 42 f"OpenSSL version: {OPENSSL_VERSION}, FIPS: {backend._fips_enabled}", 43 f"Local IP: {get_local_ip()} (Detected as {get_local_ip(override=False)})", 44 ] 45 46 47def pytest_collection_modifyitems(config: Config, items: list[Item]) -> None: 48 current_id = int(environ.get("CI_RUN_ID", "0")) - 1 49 total_ids = int(environ.get("CI_TOTAL_RUNS", "0")) 50 51 if total_ids: 52 num_tests = len(items) 53 matrix_size = math.ceil(num_tests / total_ids) 54 55 start = current_id * matrix_size 56 end = (current_id + 1) * matrix_size 57 58 deselected_items = items[:start] + items[end:] 59 config.hook.pytest_deselected(items=deselected_items) 60 items[:] = items[start:end] 61 print(f" Executing {start} - {end} tests") 62 63 64@pytest.hookimpl(trylast=True) 65def pytest_configure(config: Config): 66 # Replace the default terminal reporter 67 reporter = config.pluginmanager.get_plugin("terminalreporter") 68 if reporter: 69 config.pluginmanager.unregister(reporter) 70 config.pluginmanager.register( 71 RelativeTimeTerminalReporter(config), 72 "terminalreporter", 73 ) 74 75 76class RelativeTimeTerminalReporter(TerminalReporter): 77 _start_time: None | float 78 79 def __init__(self, config: Config, file: TextIO | None = None): 80 super().__init__(config, file) 81 self._start_time = None 82 83 def pytest_runtest_logstart(self, nodeid, location): 84 # Set start time on the first test 85 if self._start_time is None: 86 self._start_time = monotonic() 87 super().pytest_runtest_logstart(nodeid, location) 88 89 def _locationline(self, nodeid, fspath, lineno, domain): 90 original = super()._locationline(nodeid, fspath, lineno, domain) 91 if self._start_time is None: 92 return original 93 elapsed = monotonic() - self._start_time 94 minutes, seconds = divmod(elapsed, 60) 95 timestamp = f"{int(minutes):02d}:{seconds:06.3f}" 96 return f"[+{timestamp}] {original}"
IS_CI =
True
@pytest.hookimpl(hookwrapper=True)
def
pytest_sessionstart(*_, **__):
18@pytest.hookimpl(hookwrapper=True) 19def pytest_sessionstart(*_, **__): 20 """Clear the console ahead of the pytest output starting""" 21 if not IS_CI: 22 print("\x1b[2J\x1b[H") 23 # Pre-warm cryptography's PyO3 PyDateTime type cache with the real 24 # datetime class. If the first extraction happens under @freeze_time 25 # (e.g. in MTLSStageTests), PyO3 caches freezegun's FakeDatetime, 26 # which breaks every later test that passes a real datetime into 27 # cryptography ("TypeError: 'datetime' object is not an instance 28 # of 'FakeDatetime'"). The discard is intentional — only side 29 # effect needed is the type-cache initialization. 30 from datetime import UTC, datetime 31 32 from cryptography.x509.verification import PolicyBuilder 33 34 PolicyBuilder().time(datetime.now(tz=UTC)) 35 yield
Clear the console ahead of the pytest output starting
@pytest.hookimpl(trylast=True)
def
pytest_report_header(*_, **__):
38@pytest.hookimpl(trylast=True) 39def pytest_report_header(*_, **__): 40 """Add authentik version to pytest output""" 41 return [ 42 f"authentik version: {authentik_full_version()}", 43 f"OpenSSL version: {OPENSSL_VERSION}, FIPS: {backend._fips_enabled}", 44 f"Local IP: {get_local_ip()} (Detected as {get_local_ip(override=False)})", 45 ]
Add authentik version to pytest output
def
pytest_collection_modifyitems(config: _pytest.config.Config, items: list[_pytest.nodes.Item]) -> None:
48def pytest_collection_modifyitems(config: Config, items: list[Item]) -> None: 49 current_id = int(environ.get("CI_RUN_ID", "0")) - 1 50 total_ids = int(environ.get("CI_TOTAL_RUNS", "0")) 51 52 if total_ids: 53 num_tests = len(items) 54 matrix_size = math.ceil(num_tests / total_ids) 55 56 start = current_id * matrix_size 57 end = (current_id + 1) * matrix_size 58 59 deselected_items = items[:start] + items[end:] 60 config.hook.pytest_deselected(items=deselected_items) 61 items[:] = items[start:end] 62 print(f" Executing {start} - {end} tests")
@pytest.hookimpl(trylast=True)
def
pytest_configure(config: _pytest.config.Config):
65@pytest.hookimpl(trylast=True) 66def pytest_configure(config: Config): 67 # Replace the default terminal reporter 68 reporter = config.pluginmanager.get_plugin("terminalreporter") 69 if reporter: 70 config.pluginmanager.unregister(reporter) 71 config.pluginmanager.register( 72 RelativeTimeTerminalReporter(config), 73 "terminalreporter", 74 )
class
RelativeTimeTerminalReporter(_pytest.terminal.TerminalReporter):
77class RelativeTimeTerminalReporter(TerminalReporter): 78 _start_time: None | float 79 80 def __init__(self, config: Config, file: TextIO | None = None): 81 super().__init__(config, file) 82 self._start_time = None 83 84 def pytest_runtest_logstart(self, nodeid, location): 85 # Set start time on the first test 86 if self._start_time is None: 87 self._start_time = monotonic() 88 super().pytest_runtest_logstart(nodeid, location) 89 90 def _locationline(self, nodeid, fspath, lineno, domain): 91 original = super()._locationline(nodeid, fspath, lineno, domain) 92 if self._start_time is None: 93 return original 94 elapsed = monotonic() - self._start_time 95 minutes, seconds = divmod(elapsed, 60) 96 timestamp = f"{int(minutes):02d}:{seconds:06.3f}" 97 return f"[+{timestamp}] {original}"