testcontainers_modules/mariadb/
mod.rs1use std::borrow::Cow;
2
3use testcontainers::{core::WaitFor, CopyDataSource, CopyToContainer, Image};
4
5const NAME: &str = "mariadb";
6const TAG: &str = "11.3";
7
8#[derive(Debug, Default, Clone)]
29pub struct Mariadb {
30 copy_to_sources: Vec<CopyToContainer>,
31}
32
33impl Mariadb {
34 pub fn with_init_sql(mut self, init_sql: impl Into<CopyDataSource>) -> Self {
54 let target = format!(
55 "/docker-entrypoint-initdb.d/init_{i}.sql",
56 i = self.copy_to_sources.len()
57 );
58 self.copy_to_sources
59 .push(CopyToContainer::new(init_sql.into(), target));
60 self
61 }
62}
63
64impl Image for Mariadb {
65 fn name(&self) -> &str {
66 NAME
67 }
68
69 fn tag(&self) -> &str {
70 TAG
71 }
72
73 fn ready_conditions(&self) -> Vec<WaitFor> {
74 vec![
75 WaitFor::message_on_stderr("mariadbd: ready for connections."),
76 WaitFor::message_on_stderr("port: 3306"),
77 ]
78 }
79
80 fn env_vars(
81 &self,
82 ) -> impl IntoIterator<Item = (impl Into<Cow<'_, str>>, impl Into<Cow<'_, str>>)> {
83 [
84 ("MARIADB_DATABASE", "test"),
85 ("MARIADB_ALLOW_EMPTY_ROOT_PASSWORD", "1"),
86 ]
87 }
88 fn copy_to_sources(&self) -> impl IntoIterator<Item = &CopyToContainer> {
89 &self.copy_to_sources
90 }
91}
92
93#[cfg(test)]
94mod tests {
95 use mysql::prelude::Queryable;
96 use testcontainers::core::IntoContainerPort;
97
98 use crate::{
99 mariadb::Mariadb as MariadbImage,
100 testcontainers::{runners::SyncRunner, ImageExt},
101 };
102
103 #[test]
104 fn mariadb_with_init_sql() -> Result<(), Box<dyn std::error::Error + 'static>> {
105 let node = MariadbImage::default()
106 .with_init_sql(
107 "CREATE TABLE foo (bar varchar(255));"
108 .to_string()
109 .into_bytes(),
110 )
111 .start()?;
112
113 let connection_string = &format!(
114 "mysql://root@{}:{}/test",
115 node.get_host()?,
116 node.get_host_port_ipv4(3306.tcp())?
117 );
118 let mut conn = mysql::Conn::new(mysql::Opts::from_url(connection_string).unwrap()).unwrap();
119
120 let rows: Vec<String> = conn.query("INSERT INTO foo(bar) VALUES ('blub')").unwrap();
121 assert_eq!(rows.len(), 0);
122
123 let rows: Vec<String> = conn.query("SELECT bar FROM foo").unwrap();
124 assert_eq!(rows.len(), 1);
125 Ok(())
126 }
127 #[test]
128 fn mariadb_one_plus_one() -> Result<(), Box<dyn std::error::Error + 'static>> {
129 let mariadb_image = MariadbImage::default();
130 let node = mariadb_image.start()?;
131
132 let connection_string = &format!(
133 "mysql://root@{}:{}/test",
134 node.get_host()?,
135 node.get_host_port_ipv4(3306.tcp())?
136 );
137 let mut conn = mysql::Conn::new(mysql::Opts::from_url(connection_string).unwrap()).unwrap();
138
139 let first_row = conn.query_first("SELECT 1 + 1;").unwrap();
140 assert_eq!(first_row, Some(2));
141
142 let first_column: i32 = first_row.unwrap();
143 assert_eq!(first_column, 2);
144 Ok(())
145 }
146
147 #[test]
148 fn mariadb_custom_version() -> Result<(), Box<dyn std::error::Error + 'static>> {
149 let image = MariadbImage::default().with_tag("11.2.3");
150 let node = image.start()?;
151
152 let connection_string = &format!(
153 "mysql://root@{}:{}/test",
154 node.get_host()?,
155 node.get_host_port_ipv4(3306.tcp())?
156 );
157
158 let mut conn = mysql::Conn::new(mysql::Opts::from_url(connection_string).unwrap()).unwrap();
159 let first_row: Option<String> = conn.query_first("SELECT version()").unwrap();
160 let first_column: String = first_row.unwrap();
161 assert!(
162 first_column.starts_with("11.2.3"),
163 "Expected version to start with 11.2.3, got: {first_column}",
164 );
165 Ok(())
166 }
167}