Skip to content

Commit fb27121

Browse files
committed
Add container for MongoDb Atlas Local
1 parent c785ecd commit fb27121

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
@@ -18,6 +18,7 @@
1818

1919
from testcontainers.core.generic import DbContainer
2020
from testcontainers.core.utils import raise_for_deprecated_parameter
21+
from testcontainers.core.wait_strategies import HealthcheckWaitStrategy
2122
from testcontainers.core.waiting_utils import wait_for_logs
2223

2324

@@ -87,3 +88,94 @@ def predicate(text: str) -> bool:
8788

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