from __future__ import annotations
import contextlib
from dataclasses import dataclass
from typing import TYPE_CHECKING
import oracledb
import pytest
from pytest_databases.helpers import get_xdist_worker_num
from pytest_databases.types import ServiceContainer
if TYPE_CHECKING:
    from collections.abc import Generator
    from pytest_databases._service import DockerService
[docs]
def oracle_responsive(host: str, port: int, service_name: str, user: str, password: str) -> bool:
    try:
        conn = oracledb.connect(
            host=host,
            port=port,
            user=user,
            service_name=service_name,
            password=password,
        )
        with conn.cursor() as cursor:
            cursor.execute("SELECT 1 FROM dual")
            resp = cursor.fetchone()
        return resp[0] == 1 if resp is not None else False
    except Exception:  # noqa: BLE001
        return False 
[docs]
@dataclass
class OracleService(ServiceContainer):
    user: str
    password: str
    system_password: str
    service_name: str 
@contextlib.contextmanager
def _provide_oracle_service(
    docker_service: DockerService,
    image: str,
    name: str,
    service_name: str,
) -> Generator[OracleService, None, None]:
    user = "app"
    password = "super-secret"
    system_password = "super-secret"
    def check(_service: ServiceContainer) -> bool:
        try:
            conn = oracledb.connect(
                host=_service.host,
                port=_service.port,
                user=user,
                password=password,
                service_name=service_name,
            )
            with conn.cursor() as cursor:
                cursor.execute("SELECT 1 FROM dual")
                resp = cursor.fetchone()
            return resp[0] == 1 if resp is not None else False
        except Exception:  # noqa: BLE001
            return False
    worker_num = get_xdist_worker_num()
    if worker_num is not None:
        name = f"{name}_{worker_num}"
    with docker_service.run(
        image=image,
        name=name,
        check=check,
        container_port=1521,
        timeout=60,
        env={
            "ORACLE_PASSWORD": system_password,
            "APP_USER_PASSWORD": password,
            "APP_USER": user,
        },
    ) as service:
        yield OracleService(
            host=service.host,
            port=service.port,
            system_password=system_password,
            user=user,
            password=password,
            service_name=service_name,
        )
[docs]
@pytest.fixture(autouse=False, scope="session")
def oracle_23ai_image() -> str:
    return "gvenzl/oracle-free:23-slim-faststart" 
[docs]
@pytest.fixture(autouse=False, scope="session")
def oracle_23ai_service_name() -> str:
    return "FREEPDB1" 
[docs]
@pytest.fixture(autouse=False, scope="session")
def oracle_18c_image() -> str:
    return "gvenzl/oracle-xe:18-slim-faststart" 
[docs]
@pytest.fixture(autouse=False, scope="session")
def oracle_18c_service_name() -> str:
    return "xepdb1" 
[docs]
@pytest.fixture(autouse=False, scope="session")
def oracle_23ai_service(
    docker_service: DockerService, oracle_23ai_image: str, oracle_23ai_service_name: str
) -> Generator[OracleService, None, None]:
    with _provide_oracle_service(
        image=oracle_23ai_image,
        name="oracle23ai",
        service_name=oracle_23ai_service_name,
        docker_service=docker_service,
    ) as service:
        yield service 
[docs]
@pytest.fixture(autouse=False, scope="session")
def oracle_18c_service(
    docker_service: DockerService, oracle_18c_image: str, oracle_18c_service_name: str
) -> Generator[OracleService, None, None]:
    with _provide_oracle_service(
        image=oracle_18c_image,
        name="oracle18c",
        service_name=oracle_18c_service_name,
        docker_service=docker_service,
    ) as service:
        yield service 
# alias to the latest
[docs]
@pytest.fixture(autouse=False, scope="session")
def oracle_service(oracle_23ai_service: OracleService) -> OracleService:
    return oracle_23ai_service 
[docs]
@pytest.fixture(autouse=False, scope="session")
def oracle_18c_connection(
    oracle_18c_service: OracleService,
) -> Generator[oracledb.Connection, None, None]:
    with oracledb.connect(
        host=oracle_18c_service.host,
        port=oracle_18c_service.port,
        user=oracle_18c_service.user,
        service_name=oracle_18c_service.service_name,
        password=oracle_18c_service.password,
    ) as db_connection:
        yield db_connection 
[docs]
@pytest.fixture(autouse=False, scope="session")
def oracle_23ai_connection(
    oracle_23ai_service: OracleService,
) -> Generator[oracledb.Connection, None, None]:
    with oracledb.connect(
        host=oracle_23ai_service.host,
        port=oracle_23ai_service.port,
        user=oracle_23ai_service.user,
        service_name=oracle_23ai_service.service_name,
        password=oracle_23ai_service.password,
    ) as db_connection:
        yield db_connection 
[docs]
@pytest.fixture(autouse=False, scope="session")
def oracle_startup_connection(oracle_23ai_startup_connection: oracledb.Connection) -> oracledb.Connection:
    return oracle_23ai_startup_connection