Source code for pytest_databases.docker.mongodb

from __future__ import annotations

import contextlib
import traceback
from collections.abc import Generator
from dataclasses import dataclass
from typing import TYPE_CHECKING

import pymongo
import pytest
from pymongo.errors import ConnectionFailure

from pytest_databases.helpers import get_xdist_worker_num
from pytest_databases.types import ServiceContainer, XdistIsolationLevel

if TYPE_CHECKING:
    from collections.abc import Generator

    from pymongo import MongoClient
    from pymongo.database import Database

    from pytest_databases._service import DockerService


[docs] @dataclass class MongoDBService(ServiceContainer): username: str password: str database: str
[docs] @pytest.fixture(scope="session") def xdist_mongodb_isolation_level() -> XdistIsolationLevel: return "database"
@contextlib.contextmanager def _provide_mongodb_service( docker_service: DockerService, image: str, name: str, isolation_level: XdistIsolationLevel, ) -> Generator[MongoDBService, None, None]: username = "mongo_user" password = "mongo_password" default_database_name = "pytest_db" container_name = name database_name = default_database_name worker_num = get_xdist_worker_num() if worker_num is not None: suffix = f"_{worker_num}" if isolation_level == "server": container_name += suffix else: database_name += suffix def check(_service: ServiceContainer) -> bool: client: MongoClient | None = None try: client = pymongo.MongoClient( host=_service.host, port=_service.port, username=username, password=password, serverSelectionTimeoutMS=2000, # Increased timeout for robust check connectTimeoutMS=2000, socketTimeoutMS=2000, ) client.admin.command("ping") except ConnectionFailure: traceback.print_exc() return False else: return True finally: if client: client.close() with docker_service.run( image=image, name=container_name, container_port=27017, env={ "MONGO_INITDB_ROOT_USERNAME": username, "MONGO_INITDB_ROOT_PASSWORD": password, }, check=check, pause=0.5, timeout=120, transient=isolation_level == "server", ) as service: yield MongoDBService( host=service.host, port=service.port, username=username, password=password, database=database_name )
[docs] @pytest.fixture(autouse=False, scope="session") def mongodb_image() -> str: return "mongo:latest"
[docs] @pytest.fixture(autouse=False, scope="session") def mongodb_service( docker_service: DockerService, xdist_mongodb_isolation_level: XdistIsolationLevel, mongodb_image: str, ) -> Generator[MongoDBService, None, None]: with _provide_mongodb_service(docker_service, mongodb_image, "mongodb", xdist_mongodb_isolation_level) as service: yield service
[docs] @pytest.fixture(autouse=False, scope="session") def mongodb_connection(mongodb_service: MongoDBService) -> Generator[MongoClient, None, None]: client: MongoClient | None = None try: client = pymongo.MongoClient( host=mongodb_service.host, port=mongodb_service.port, username=mongodb_service.username, password=mongodb_service.password, ) yield client finally: if client: client.close()
[docs] @pytest.fixture(autouse=False, scope="function") def mongodb_database( mongodb_connection: MongoClient, mongodb_service: MongoDBService ) -> Generator[Database, None, None]: """Provides a MongoDB database instance for testing. Yields: A MongoDB database instance. """ db = mongodb_connection[mongodb_service.database] yield db
# For a truly clean state per test, you might consider dropping the database here, # but it depends on the desired test isolation and speed. # e.g., mongodb_connection.drop_database(mongodb_service.database)