Skip to content

Commit 71edfe1

Browse files
committed
Add container for MongoDb Atlas Local
1 parent aa47435 commit 71edfe1

File tree

3 files changed

+142
-1
lines changed

3 files changed

+142
-1
lines changed

modules/mongodb/README.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
.. autoclass:: testcontainers.mongodb.MongoDbContainer
2+
.. autoclass:: testcontainers.mongodb.MongoDBAtlasLocalContainer
23
.. title:: testcontainers.mongodb.MongoDbContainer

modules/mongodb/testcontainers/mongodb/__init__.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
from testcontainers.core.generic import DbContainer
1919
from testcontainers.core.utils import raise_for_deprecated_parameter
20+
from testcontainers.core.wait_strategies import HealthcheckWaitStrategy
2021
from testcontainers.core.waiting_utils import wait_for_logs
2122

2223

@@ -81,3 +82,94 @@ def _connect(self) -> None:
8182

8283
def get_connection_client(self) -> MongoClient:
8384
return MongoClient(self.get_connection_url())
85+
86+
87+
class MongoDBAtlasLocalContainer(DbContainer):
88+
"""
89+
MongoDB Atlas Local document-based database container.
90+
91+
This is the local version of the Mongo Atlas service.
92+
It includes Mongo DB and Mongo Atlas Search services
93+
Example:
94+
95+
.. doctest::
96+
97+
>>> from testcontainers.mongodb import MongoDBAtlasLocalContainer
98+
>>> import time
99+
>>> with MongoDBAtlasLocalContainer("mongodb/mongodb-atlas-local:8.0.13") as mongo:
100+
... db = mongo.get_connection_client().test
101+
... # Insert a database entry
102+
... result = db.restaurants.insert_one(
103+
... {
104+
... "name": "Vella",
105+
... "cuisine": "Italian",
106+
... "restaurant_id": "123456"
107+
... }
108+
... )
109+
... # add an index
110+
... db.restaurants.create_search_index(
111+
... {
112+
... "definition": {
113+
... "mappings": {
114+
... "dynamic": True
115+
... }
116+
... },
117+
... "name": "default"
118+
... }
119+
... )
120+
... # wait for the index to be created
121+
... time.sleep(1)
122+
...
123+
... # Find the restaurant document
124+
... result = db.restaurants.aggregate([{
125+
... "$search": {
126+
... "index": "default",
127+
... "text": {
128+
... "query": "Vella",
129+
... "path": "name"
130+
... }
131+
... }
132+
... }]).next()
133+
... result["restaurant_id"]
134+
'123456'
135+
"""
136+
137+
def __init__(
138+
self,
139+
image: str = "mongodb/mongodb-atlas-local:latest",
140+
port: int = 27017,
141+
username: Optional[str] = None,
142+
password: Optional[str] = None,
143+
dbname: Optional[str] = None,
144+
**kwargs,
145+
) -> None:
146+
raise_for_deprecated_parameter(kwargs, "port_to_expose", "port")
147+
super().__init__(image=image, **kwargs)
148+
self.username = username if username else os.environ.get("MONGODB_INITDB_ROOT_USERNAME", "test")
149+
self.password = password if password else os.environ.get("MONGODB_INITDB_ROOT_PASSWORD", "test")
150+
self.dbname = dbname if dbname else os.environ.get("MONGODB_INITDB_DATABASE", "test")
151+
self.port = port
152+
self.with_exposed_ports(self.port)
153+
154+
def _configure(self) -> None:
155+
self.with_env("MONGODB_INITDB_ROOT_USERNAME", self.username)
156+
self.with_env("MONGODB_INITDB_ROOT_PASSWORD", self.password)
157+
self.with_env("MONGODB_INITDB_DATABASE", self.dbname)
158+
159+
def get_connection_url(self) -> str:
160+
return (
161+
self._create_connection_url(
162+
dialect="mongodb",
163+
username=self.username,
164+
password=self.password,
165+
port=self.port,
166+
)
167+
+ "?directConnection=true"
168+
)
169+
170+
def _connect(self) -> None:
171+
strategy = HealthcheckWaitStrategy()
172+
strategy.wait_until_ready(self)
173+
174+
def get_connection_client(self) -> MongoClient:
175+
return MongoClient(self.get_connection_url())

modules/mongodb/tests/test_mongodb.py

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
import time
12
import pytest
23
from pymongo import MongoClient
34
from pymongo.errors import OperationFailure
45

5-
from testcontainers.mongodb import MongoDbContainer
6+
from testcontainers.mongodb import MongoDbContainer, MongoDBAtlasLocalContainer
67

78

89
@pytest.mark.parametrize("version", ["7.0.7", "6.0.14", "5.0.26"])
@@ -28,6 +29,53 @@ def test_docker_run_mongodb(version: str):
2829
assert cursor.next()["restaurant_id"] == doc["restaurant_id"]
2930

3031

32+
@pytest.mark.parametrize("version", ["8.0.13", "7.0.23"])
33+
def test_docker_run_mongodb_atlas_local(version: str):
34+
with MongoDBAtlasLocalContainer(f"mongodb/mongodb-atlas-local:{version}") as mongo_atlas:
35+
db = mongo_atlas.get_connection_client().test
36+
index_doc = {
37+
"definition": {
38+
"mappings": {
39+
"dynamic": False,
40+
"fields": {
41+
"borough": {"analyzer": "lucene.keyword", "type": "string"},
42+
},
43+
},
44+
},
45+
"name": "test",
46+
}
47+
48+
db.create_collection("restaurants")
49+
50+
db.restaurants.create_search_index(index_doc)
51+
52+
doc = {
53+
"address": {
54+
"street": "2 Avenue",
55+
"zipcode": "10075",
56+
"building": "1480",
57+
"coord": [-73.9557413, 40.7720266],
58+
},
59+
"borough": "Manhattan",
60+
"cuisine": "Italian",
61+
"name": "Vella",
62+
"restaurant_id": "41704620",
63+
}
64+
result = db.restaurants.insert_one(doc)
65+
assert result.inserted_id
66+
67+
# Wait for index to catch up
68+
indexes = db.restaurants.list_search_indexes()
69+
while indexes.next()["status"] != "READY":
70+
time.sleep(0.1)
71+
indexes = db.restaurants.list_search_indexes()
72+
73+
cursor = db.restaurants.aggregate(
74+
[{"$search": {"index": "test", "text": {"query": "Manhattan", "path": "borough"}}}]
75+
)
76+
assert cursor.next()["restaurant_id"] == doc["restaurant_id"]
77+
78+
3179
# This is a feature in the generic DbContainer class
3280
# but it can't be tested on its own
3381
# so is tested in various database modules:

0 commit comments

Comments
 (0)