Loading pyproject.toml +4 −6 Changes for pyproject.toml: 4 added lines, 6 removed lines. Original line number Diff line number Diff line Loading @@ -72,14 +72,12 @@ source_modules = ["portal_service.application"] forbidden_modules = ["portal_service.api", "portal_service.adapters"] [tool.pytest.ini_options] asyncio_mode = "auto" cache_dir = ".cache/pytest" # Integration tests need a running Keycloak, so a plain `pytest` leaves them out. # `pytest -m integration` (or `make integration`) runs them: the -m on the command # line replaces the one here. addopts = "-m 'not integration'" testpaths = ["tests"] pythonpath = ["."] asyncio_mode = "auto" markers = [ "integration: needs a live Keycloak reachable at KEYCLOAK_ISSUER", "integration: tests that require a running Docker daemon (testcontainers)", ] [tool.coverage.run] Loading tests/integration/conftest.py +8 −0 Changes for tests/integration/conftest.py: 8 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -12,12 +12,20 @@ from pathlib import Path import httpx import pytest from pytest import Item from sqlalchemy import text from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine from testcontainers.core.container import DockerContainer from portal_service.adapters.database.sql import get_metadata def pytest_collection_modifyitems(items: list[Item]) -> None: for item in items: if "tests/integration" in str(item.fspath): item.add_marker(pytest.mark.integration) POSTGRES_IMAGE = "postgres:16-alpine" Loading tests/integration/test_concurrent_refresh.py +0 −2 Changes for tests/integration/test_concurrent_refresh.py: 0 added lines, 2 removed lines. Original line number Diff line number Diff line Loading @@ -22,8 +22,6 @@ from portal_service.domain.errors import InvalidGrantError from portal_service.domain.models import IssuedTokens, LoginSession, SessionTokens from tests.fakes import FakeIdentityProvider pytestmark = pytest.mark.integration class RotatingIdentityProvider(FakeIdentityProvider): """Keycloak with refresh-token rotation on: each refresh token works once.""" Loading tests/integration/test_keycloak_contract.py +8 −7 Changes for tests/integration/test_keycloak_contract.py: 8 added lines, 7 removed lines. Original line number Diff line number Diff line Loading @@ -5,8 +5,10 @@ Unit tests fake the network, so they cannot notice a Keycloak upgrade that moves endpoint, drops an algorithm or changes how clients authenticate. This reads the realm's own discovery document and compares it with what the client would do. Needs a running Keycloak: make integration Override the realm with: KEYCLOAK_ISSUER=http://host:port/realms/<realm> It checks a deployed realm, so it only runs when told where one is, and is skipped otherwise (in CI, and on a machine without the cluster): KEYCLOAK_ISSUER=http://localhost:30081/realms/oop make integration """ import os Loading @@ -16,14 +18,13 @@ import pytest from portal_service.config import OIDCSettings pytestmark = pytest.mark.integration DEFAULT_ISSUER = "http://localhost:30081/realms/oop" @pytest.fixture(scope="module") def issuer() -> str: return os.environ.get("KEYCLOAK_ISSUER", DEFAULT_ISSUER) issuer = os.environ.get("KEYCLOAK_ISSUER") if not issuer: pytest.skip("KEYCLOAK_ISSUER not set: no deployed realm to check") return issuer @pytest.fixture(scope="module") Loading tests/integration/test_keycloak_flow.py +0 −2 Changes for tests/integration/test_keycloak_flow.py: 0 added lines, 2 removed lines. Original line number Diff line number Diff line Loading @@ -22,8 +22,6 @@ from portal_service.application.pkce import code_challenge, new_code_verifier, n from portal_service.config import OIDCSettings from portal_service.domain.errors import IdentityProviderUnavailableError, InvalidGrantError pytestmark = pytest.mark.integration CLIENT_ID = "oop-portal" CLIENT_SECRET = "test-client-secret" REDIRECT_URI = "http://localhost:8000/api/auth/callback" Loading Loading
pyproject.toml +4 −6 Changes for pyproject.toml: 4 added lines, 6 removed lines. Original line number Diff line number Diff line Loading @@ -72,14 +72,12 @@ source_modules = ["portal_service.application"] forbidden_modules = ["portal_service.api", "portal_service.adapters"] [tool.pytest.ini_options] asyncio_mode = "auto" cache_dir = ".cache/pytest" # Integration tests need a running Keycloak, so a plain `pytest` leaves them out. # `pytest -m integration` (or `make integration`) runs them: the -m on the command # line replaces the one here. addopts = "-m 'not integration'" testpaths = ["tests"] pythonpath = ["."] asyncio_mode = "auto" markers = [ "integration: needs a live Keycloak reachable at KEYCLOAK_ISSUER", "integration: tests that require a running Docker daemon (testcontainers)", ] [tool.coverage.run] Loading
tests/integration/conftest.py +8 −0 Changes for tests/integration/conftest.py: 8 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -12,12 +12,20 @@ from pathlib import Path import httpx import pytest from pytest import Item from sqlalchemy import text from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine from testcontainers.core.container import DockerContainer from portal_service.adapters.database.sql import get_metadata def pytest_collection_modifyitems(items: list[Item]) -> None: for item in items: if "tests/integration" in str(item.fspath): item.add_marker(pytest.mark.integration) POSTGRES_IMAGE = "postgres:16-alpine" Loading
tests/integration/test_concurrent_refresh.py +0 −2 Changes for tests/integration/test_concurrent_refresh.py: 0 added lines, 2 removed lines. Original line number Diff line number Diff line Loading @@ -22,8 +22,6 @@ from portal_service.domain.errors import InvalidGrantError from portal_service.domain.models import IssuedTokens, LoginSession, SessionTokens from tests.fakes import FakeIdentityProvider pytestmark = pytest.mark.integration class RotatingIdentityProvider(FakeIdentityProvider): """Keycloak with refresh-token rotation on: each refresh token works once.""" Loading
tests/integration/test_keycloak_contract.py +8 −7 Changes for tests/integration/test_keycloak_contract.py: 8 added lines, 7 removed lines. Original line number Diff line number Diff line Loading @@ -5,8 +5,10 @@ Unit tests fake the network, so they cannot notice a Keycloak upgrade that moves endpoint, drops an algorithm or changes how clients authenticate. This reads the realm's own discovery document and compares it with what the client would do. Needs a running Keycloak: make integration Override the realm with: KEYCLOAK_ISSUER=http://host:port/realms/<realm> It checks a deployed realm, so it only runs when told where one is, and is skipped otherwise (in CI, and on a machine without the cluster): KEYCLOAK_ISSUER=http://localhost:30081/realms/oop make integration """ import os Loading @@ -16,14 +18,13 @@ import pytest from portal_service.config import OIDCSettings pytestmark = pytest.mark.integration DEFAULT_ISSUER = "http://localhost:30081/realms/oop" @pytest.fixture(scope="module") def issuer() -> str: return os.environ.get("KEYCLOAK_ISSUER", DEFAULT_ISSUER) issuer = os.environ.get("KEYCLOAK_ISSUER") if not issuer: pytest.skip("KEYCLOAK_ISSUER not set: no deployed realm to check") return issuer @pytest.fixture(scope="module") Loading
tests/integration/test_keycloak_flow.py +0 −2 Changes for tests/integration/test_keycloak_flow.py: 0 added lines, 2 removed lines. Original line number Diff line number Diff line Loading @@ -22,8 +22,6 @@ from portal_service.application.pkce import code_challenge, new_code_verifier, n from portal_service.config import OIDCSettings from portal_service.domain.errors import IdentityProviderUnavailableError, InvalidGrantError pytestmark = pytest.mark.integration CLIENT_ID = "oop-portal" CLIENT_SECRET = "test-client-secret" REDIRECT_URI = "http://localhost:8000/api/auth/callback" Loading