testcontainers_modules/mongo/
mod.rs1use testcontainers::{
2 core::{CmdWaitFor, ExecCommand, WaitFor},
3 Image,
4};
5
6const NAME: &str = "mongo";
7const TAG: &str = "5.0.6";
8
9#[derive(Default, Debug, Clone)]
11enum InstanceKind {
12 #[default]
14 Standalone,
15 ReplSet,
17}
18
19#[derive(Default, Debug, Clone)]
40pub struct Mongo {
41 kind: InstanceKind,
42}
43
44impl Mongo {
45 pub fn new() -> Self {
56 Self {
57 kind: InstanceKind::Standalone,
58 }
59 }
60 pub fn repl_set() -> Self {
72 Self {
73 kind: InstanceKind::ReplSet,
74 }
75 }
76}
77
78impl Image for Mongo {
79 fn name(&self) -> &str {
80 NAME
81 }
82
83 fn tag(&self) -> &str {
84 TAG
85 }
86
87 fn ready_conditions(&self) -> Vec<WaitFor> {
88 vec![WaitFor::message_on_stdout("Waiting for connections")]
89 }
90
91 fn cmd(&self) -> impl IntoIterator<Item = impl Into<std::borrow::Cow<'_, str>>> {
92 match self.kind {
93 InstanceKind::Standalone => Vec::<String>::new(),
94 InstanceKind::ReplSet => vec!["--replSet".to_string(), "rs".to_string()],
95 }
96 }
97
98 fn exec_after_start(
99 &self,
100 _: testcontainers::core::ContainerState,
101 ) -> Result<Vec<ExecCommand>, testcontainers::TestcontainersError> {
102 match self.kind {
103 InstanceKind::Standalone => Ok(Default::default()),
104 InstanceKind::ReplSet => Ok(vec![ExecCommand::new(vec![
105 "mongosh".to_string(),
106 "--quiet".to_string(),
107 "--eval".to_string(),
108 "'rs.initiate()'".to_string(),
109 ])
110 .with_cmd_ready_condition(CmdWaitFor::message_on_stdout(
111 "Using a default configuration for the set",
112 ))
113 .with_container_ready_conditions(vec![WaitFor::message_on_stdout(
114 "Rebuilding PrimaryOnlyService due to stepUp",
115 )])]),
116 }
117 }
118}
119
120#[cfg(test)]
121mod tests {
122 use mongodb::*;
123 use testcontainers::{core::IntoContainerPort, runners::AsyncRunner};
124
125 use crate::mongo;
126
127 #[tokio::test]
128 async fn mongo_fetch_document() -> Result<(), Box<dyn std::error::Error + 'static>> {
129 let _ = pretty_env_logger::try_init();
130 let node = mongo::Mongo::default().start().await?;
131 let host_ip = node.get_host().await?;
132 let host_port = node.get_host_port_ipv4(27017.tcp()).await?;
133 let url = format!("mongodb://{host_ip}:{host_port}/");
134
135 let client: Client = Client::with_uri_str(&url).await.unwrap();
136 let db = client.database("some_db");
137 let coll = db.collection("some_coll");
138
139 let insert_one_result = coll.insert_one(bson::doc! { "x": 42 }).await.unwrap();
140 assert!(!insert_one_result
141 .inserted_id
142 .as_object_id()
143 .unwrap()
144 .to_hex()
145 .is_empty());
146
147 let find_one_result: bson::Document = coll
148 .find_one(bson::doc! { "x": 42 })
149 .await
150 .unwrap()
151 .unwrap();
152 assert_eq!(42, find_one_result.get_i32("x").unwrap());
153
154 Ok(())
155 }
156
157 #[tokio::test]
158 async fn mongo_repl_set_fetch_document() -> Result<(), Box<dyn std::error::Error + 'static>> {
159 let _ = pretty_env_logger::try_init();
160 let node = mongo::Mongo::repl_set().start().await?;
161 let host_ip = node.get_host().await?;
162 let host_port = node.get_host_port_ipv4(27017).await?;
163 let url = format!("mongodb://{host_ip}:{host_port}/?directConnection=true",);
164
165 let client: Client = Client::with_uri_str(url).await?;
166 let db = client.database("some_db");
167 let coll = db.collection("some-coll");
168
169 let mut session = client.start_session().await?;
170 session.start_transaction().await?;
171
172 let insert_one_result = coll
173 .insert_one(bson::doc! { "x": 42 })
174 .session(&mut session)
175 .await?;
176 assert!(!insert_one_result
177 .inserted_id
178 .as_object_id()
179 .unwrap()
180 .to_hex()
181 .is_empty());
182 session.commit_transaction().await?;
183
184 let find_one_result: bson::Document = coll
185 .find_one(bson::doc! { "x": 42 })
186 .await
187 .unwrap()
188 .unwrap();
189
190 assert_eq!(42, find_one_result.get_i32("x").unwrap());
191 Ok(())
192 }
193}