diff --git a/poetry.lock b/poetry.lock index d455b7f..d2f5ce0 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.0 and should not be changed by hand. [[package]] name = "annotated-types" @@ -1210,6 +1210,40 @@ rabbitmq = ["pika"] redis = ["redis"] selenium = ["selenium"] +[[package]] +name = "testcontainers-core" +version = "0.0.1rc1" +description = "Core component of testcontainers-python." +optional = false +python-versions = ">=3.7" +files = [ + {file = "testcontainers_core-0.0.1rc1-py3-none-any.whl", hash = "sha256:69a8bf2ddb52ac2d03c26401b12c70db0453cced40372ad783d6dce417e52095"}, +] + +[package.dependencies] +docker = ">=4.0.0" +wrapt = "*" + +[[package]] +name = "testcontainers-keycloak" +version = "0.0.1rc1" +description = "Keycloak component of testcontainers-python." +optional = false +python-versions = ">=3.7" +files = [] +develop = false + +[package.dependencies] +python-keycloak = "*" +testcontainers-core = "*" + +[package.source] +type = "git" +url = "ssh://git@github.com/TheForgottened/testcontainers-python.git" +reference = "HEAD" +resolved_reference = "cb74c2c8789bd9329c48bc105f0b63e40ae4e675" +subdirectory = "keycloak" + [[package]] name = "tomli" version = "2.0.1" @@ -1355,4 +1389,4 @@ starlette-admin = ["starlette-admin"] [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "b1f307af96457fa36380ad1ab15023b9efc78bbedc1c142f0ea0ed1b2d4c9f9e" +content-hash = "eba554c131282ffe0c18fd00745190a6b744f16cd46736efe8dbe6491671b053" diff --git a/pyproject.toml b/pyproject.toml index 4b5b106..da2a2be 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,8 +31,9 @@ pyright = "^1.1.351" [tool.poetry.group.test.dependencies] pytest = "^8.0.1" pytest-mock = "^3.12.0" -testcontainers = "^3.7.1" python-keycloak = "^3.9.0" +testcontainers = "^3.7.1" +testcontainers-keycloak = { git = "ssh://git@github.com/TheForgottened/testcontainers-python.git", subdirectory = "keycloak" } # updated Keycloak container: https://github.com/TheForgottened/testcontainers-python/tree/cb74c2c8789bd9329c48bc105f0b63e40ae4e675/keycloak [build-system] requires = ["poetry-core"] diff --git a/tests/keycloak_testcontainer.py b/tests/keycloak_testcontainer.py deleted file mode 100644 index 967b04e..0000000 --- a/tests/keycloak_testcontainer.py +++ /dev/null @@ -1,111 +0,0 @@ -# source: https://github.com/testcontainers/testcontainers-python/pull/369 -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -import os -from typing import Optional - -import requests -from keycloak import KeycloakAdmin -from testcontainers.core.container import DockerContainer -from testcontainers.core.waiting_utils import wait_container_is_ready, wait_for_logs - - -class KeycloakContainer(DockerContainer): - """ - Keycloak container. - - Example: - - .. doctest:: - - >>> from testcontainers.keycloak import KeycloakContainer - - >>> with KeycloakContainer() as kc: - ... keycloak = kc.get_client() - """ - - def __init__( - self, - image="quay.io/keycloak/keycloak:latest", - username: Optional[str] = None, - password: Optional[str] = None, - port: int = 8080, - ) -> None: - super(KeycloakContainer, self).__init__(image=image) - self.username = username or os.getenv("KEYCLOAK_ADMIN", "test") - self.password = password or os.getenv("KEYCLOAK_ADMIN_PASSWORD", "test") - self.port = port - self.with_exposed_ports(self.port) - - def _get_image_tag(self) -> str: - return self.image.split(":")[1] - - def _uses_legacy_architecture(self) -> bool: - tag = self._get_image_tag() - - try: - major_version_number = int(tag.split(".")[0]) - except ValueError: - major_version_number = 2023 - - return major_version_number <= 16 or "legacy" in tag.lower() - - def _configure(self) -> None: - # There's a bit of confusion as to which version / architecture uses which - # Because of this we're setting the credentials both ways - self.with_env("KEYCLOAK_ADMIN", self.username) - self.with_env("KEYCLOAK_ADMIN_PASSWORD", self.password) - self.with_env("KEYCLOAK_USER", self.username) - self.with_env("KEYCLOAK_PASSWORD", self.password) - - self.with_env("KC_HEALTH_ENABLED", "true") - self.with_env("KC_METRICS_ENABLED", "true") - - if not self._uses_legacy_architecture(): - self.with_command("start-dev") - - def get_url(self) -> str: - host = self.get_container_host_ip() - port = self.get_exposed_port(self.port) - return f"http://{host}:{port}" - - def get_base_api_url(self) -> str: - base_url = self.get_url() - return f"{base_url}/auth" if self._uses_legacy_architecture() else base_url - - @wait_container_is_ready( # pyright: ignore - requests.exceptions.ConnectionError, requests.exceptions.ReadTimeout - ) - def _connect(self) -> None: - url = self.get_url() - response = requests.get(f"{url}/auth", timeout=1) - response.raise_for_status() - - def start(self) -> "KeycloakContainer": - self._configure() - container = super().start() - - if self._uses_legacy_architecture(): - self._connect() - else: - wait_for_logs(container, "Listening on:") - - return self - - def get_client(self, **kwargs) -> KeycloakAdmin: - return KeycloakAdmin( - server_url=f"{self.get_base_api_url()}/", - username=self.username, - password=self.password, - verify=True, - ) diff --git a/tests/test_oauth.py b/tests/test_oauth.py index 75bda54..c60defb 100644 --- a/tests/test_oauth.py +++ b/tests/test_oauth.py @@ -7,7 +7,7 @@ from keycloak import KeycloakAdmin import pytest from keycloak_oauth import KeycloakOAuth2 -from tests.keycloak_testcontainer import KeycloakContainer +from testcontainers.keycloak import KeycloakContainer from fastapi.testclient import TestClient