diff --git a/pytest.ini b/pytest.ini index 16c88ba91..0aca15e0a 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,3 +1,4 @@ # Handling DeprecationWarning 'asyncio_mode' default value [pytest] asyncio_mode = strict +asyncio_default_fixture_loop_scope = function \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 656fe7c60..da78216bb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,5 +7,6 @@ pytest-asyncio pytest-rerunfailures wheel>=0.38.0 twine +testcontainers setuptools>=70.0.0 # not directly required, pinned by Snyk to avoid a vulnerability -zipp>=3.19.1 # not directly required, pinned by Snyk to avoid a vulnerability +zipp>=3.19.1 # not directly required, pinned by Snyk to avoid a vulnerability \ No newline at end of file diff --git a/tests/.env.example b/tests/.env.example new file mode 100644 index 000000000..ae9fb7b10 --- /dev/null +++ b/tests/.env.example @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +export OPAL_POLICY_REPO_URL='' +export POLICY_REPO_BRANCH='' +export OPAL_POLICY_REPO_SSH_KEY='' diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 000000000..02985a920 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,59 @@ +import time +import docker +import pytest +from testcontainers.core.waiting_utils import wait_for_logs +from testcontainers.postgres import PostgresContainer + +from tests.containers import OpalClientContainer, OpalServerContainer + +from . import settings as s + + +@pytest.fixture(scope="session") +def opal_network(): + client = docker.from_env() + network = client.networks.create(s.OPAL_TESTS_NETWORK_NAME, driver="bridge") + yield network.name + network.remove() + + +@pytest.fixture(scope="session") +def broadcast_channel(opal_network: str): + with PostgresContainer("postgres:alpine", network=opal_network).with_name( + f"pytest_opal_brodcast_channel_{s.OPAL_TESTS_UNIQ_ID}" + ) as container: + yield container + + +@pytest.fixture(scope="session") +def opal_server(opal_network: str, broadcast_channel: PostgresContainer): + opal_broadcast_uri = broadcast_channel.get_connection_url( + host=f"{broadcast_channel._name}.{opal_network}", driver=None + ) + + with OpalServerContainer(network=opal_network).with_env( + "OPAL_BROADCAST_URI", opal_broadcast_uri + ) as container: + container.get_wrapped_container().reload() + print(container.get_wrapped_container().id) + wait_for_logs(container, "Clone succeeded") + yield container + + +@pytest.fixture(scope="session") +def opal_client(opal_network: str, opal_server: OpalServerContainer): + opal_server_url = f"http://{opal_server._name}.{opal_network}:7002" + + with OpalClientContainer(network=opal_network).with_env( + "OPAL_SERVER_URL", opal_server_url + ) as container: + wait_for_logs(container, "") + yield container + + +@pytest.fixture(scope="session", autouse=True) +def setup(opal_server, opal_client): + yield + if s.OPAL_TESTS_DEBUG: + s.dump_settings() + time.sleep(3600) # Giving us some time to inspect the containers \ No newline at end of file diff --git a/tests/containers.py b/tests/containers.py new file mode 100644 index 000000000..0e85a587d --- /dev/null +++ b/tests/containers.py @@ -0,0 +1,85 @@ +from testcontainers.core.generic import DockerContainer + +from . import settings as s + + +class OpalServerContainer(DockerContainer): + def __init__( + self, + image: str = f"permitio/opal-server:{s.OPAL_IMAGE_TAG}", + docker_client_kw: dict | None = None, + **kwargs, + ) -> None: + super().__init__(image, docker_client_kw, **kwargs) + + self.with_name(s.OPAL_TESTS_SERVER_CONTAINER_NAME) + self.with_exposed_ports(7002) + + if s.OPAL_TESTS_DEBUG: + self.with_env("LOG_DIAGNOSE", "true") + self.with_env("OPAL_LOG_LEVEL", "DEBUG") + + self.with_env("UVICORN_NUM_WORKERS", s.UVICORN_NUM_WORKERS) + + self.with_env("OPAL_POLICY_REPO_URL", s.OPAL_POLICY_REPO_URL) + self.with_env( + "OPAL_POLICY_REPO_POLLING_INTERVAL", s.OPAL_POLICY_REPO_POLLING_INTERVAL + ) + self.with_env("OPAL_POLICY_REPO_MAIN_BRANCH", s.OPAL_POLICY_REPO_MAIN_BRANCH) + if s.OPAL_POLICY_REPO_SSH_KEY: + self.with_env("OPAL_POLICY_REPO_SSH_KEY", s.OPAL_POLICY_REPO_SSH_KEY) + self.with_env( + "OPAL_POLICY_REPO_WEBHOOK_SECRET", s.OPAL_POLICY_REPO_WEBHOOK_SECRET + ) + self.with_env( + "OPAL_POLICY_REPO_WEBHOOK_PARAMS", s.OPAL_POLICY_REPO_WEBHOOK_PARAMS + ) + + self.with_env("OPAL_DATA_CONFIG_SOURCES", s.OPAL_DATA_CONFIG_SOURCES) + self.with_env("OPAL_LOG_FORMAT_INCLUDE_PID", s.OPAL_LOG_FORMAT_INCLUDE_PID) + + self.with_env("OPAL_AUTH_MASTER_TOKEN", s.OPAL_AUTH_MASTER_TOKEN) + + self.with_env("OPAL_AUTH_PUBLIC_KEY", s.OPAL_AUTH_PUBLIC_KEY) + self.with_env("OPAL_AUTH_PRIVATE_KEY", s.OPAL_AUTH_PRIVATE_KEY) + if s.OPAL_AUTH_PRIVATE_KEY_PASSPHRASE: + self.with_env( + "OPAL_AUTH_PRIVATE_KEY_PASSPHRASE", s.OPAL_AUTH_PRIVATE_KEY_PASSPHRASE + ) + self.with_env("OPAL_AUTH_JWT_AUDIENCE", s.OPAL_AUTH_JWT_AUDIENCE) + self.with_env("OPAL_AUTH_JWT_ISSUER", s.OPAL_AUTH_JWT_ISSUER) + # FIXME: The env below is triggerring: did not find main branch: main,... + # self.with_env("OPAL_STATISTICS_ENABLED", s.OPAL_STATISTICS_ENABLED) + + +class OpalClientContainer(DockerContainer): + def __init__( + self, + image: str = f"permitio/opal-client:{s.OPAL_IMAGE_TAG}", + docker_client_kw: dict | None = None, + **kwargs, + ) -> None: + super().__init__(image, docker_client_kw, **kwargs) + + self.with_name(s.OPAL_TESTS_CLIENT_CONTAINER_NAME) + self.with_exposed_ports(7000, 8181) + + if s.OPAL_TESTS_DEBUG: + self.with_env("LOG_DIAGNOSE", "true") + self.with_env("OPAL_LOG_LEVEL", "DEBUG") + + self.with_env("OPAL_LOG_FORMAT_INCLUDE_PID", s.OPAL_LOG_FORMAT_INCLUDE_PID) + self.with_env("OPAL_INLINE_OPA_LOG_FORMAT", s.OPAL_INLINE_OPA_LOG_FORMAT) + self.with_env( + "OPAL_SHOULD_REPORT_ON_DATA_UPDATES", s.OPAL_SHOULD_REPORT_ON_DATA_UPDATES + ) + self.with_env("OPAL_DEFAULT_UPDATE_CALLBACKS", s.OPAL_DEFAULT_UPDATE_CALLBACKS) + self.with_env( + "OPAL_OPA_HEALTH_CHECK_POLICY_ENABLED", + s.OPAL_OPA_HEALTH_CHECK_POLICY_ENABLED, + ) + self.with_env("OPAL_CLIENT_TOKEN", s.OPAL_CLIENT_TOKEN) + self.with_env("OPAL_AUTH_PUBLIC_KEY", s.OPAL_AUTH_PUBLIC_KEY) + self.with_env("OPAL_AUTH_JWT_AUDIENCE", s.OPAL_AUTH_JWT_AUDIENCE) + self.with_env("OPAL_AUTH_JWT_ISSUER", s.OPAL_AUTH_JWT_ISSUER) + # self.with_env("OPAL_STATISTICS_ENABLED", s.OPAL_STATISTICS_ENABLED) diff --git a/tests/run.sh b/tests/run.sh new file mode 100755 index 000000000..b49bcf318 --- /dev/null +++ b/tests/run.sh @@ -0,0 +1,38 @@ +#!/bin/bash +set -e + +if [[ -f ".env" ]]; then + # shellcheck disable=SC1091 + source .env +fi + +# TODO: Disable after debugging. +export OPAL_TESTS_DEBUG='true' +export OPAL_POLICY_REPO_URL +export OPAL_POLICY_REPO_BRANCH +export OPAL_POLICY_REPO_SSH_KEY +export OPAL_AUTH_PUBLIC_KEY +export OPAL_AUTH_PRIVATE_KEY + +OPAL_POLICY_REPO_URL=${OPAL_POLICY_REPO_URL:-git@github.com:permitio/opal-tests-policy-repo.git} +OPAL_POLICY_REPO_BRANCH=test-$RANDOM$RANDOM +OPAL_POLICY_REPO_SSH_KEY_PATH=${OPAL_POLICY_REPO_SSH_KEY_PATH:-~/.ssh/id_rsa} +OPAL_POLICY_REPO_SSH_KEY=${OPAL_POLICY_REPO_SSH_KEY:-$(cat "$OPAL_POLICY_REPO_SSH_KEY_PATH")} + +function generate_opal_keys { + echo "- Generating OPAL keys" + + ssh-keygen -q -t rsa -b 4096 -m pem -f opal_crypto_key -N "" + OPAL_AUTH_PUBLIC_KEY="$(cat opal_crypto_key.pub)" + OPAL_AUTH_PRIVATE_KEY="$(tr '\n' '_'