testcontainers_modules/azurite/
mod.rs1use std::{borrow::Cow, collections::BTreeMap};
2
3use testcontainers::{
4 core::{ContainerPort, WaitFor},
5 Image,
6};
7
8const NAME: &str = "mcr.microsoft.com/azure-storage/azurite";
9const TAG: &str = "3.34.0";
10
11pub const BLOB_PORT: ContainerPort = ContainerPort::Tcp(10000);
13
14pub const QUEUE_PORT: ContainerPort = ContainerPort::Tcp(10001);
16
17const TABLE_PORT: ContainerPort = ContainerPort::Tcp(10002);
19
20const AZURITE_ACCOUNTS: &str = "AZURITE_ACCOUNTS";
21
22#[derive(Debug, Default, Clone)]
43pub struct Azurite {
44 env_vars: BTreeMap<String, String>,
45 loose: bool,
46 skip_api_version_check: bool,
47 disable_telemetry: bool,
48}
49
50impl Azurite {
51 pub fn with_accounts(self, accounts: String) -> Self {
56 let mut env_vars = self.env_vars;
57 env_vars.insert(AZURITE_ACCOUNTS.to_owned(), accounts);
58 Self { env_vars, ..self }
59 }
60
61 pub fn with_loose(self) -> Self {
63 Self {
64 loose: true,
65 ..self
66 }
67 }
68
69 pub fn with_skip_api_version_check(self) -> Self {
71 Self {
72 skip_api_version_check: true,
73 ..self
74 }
75 }
76
77 pub fn with_disable_telemetry(self) -> Self {
79 Self {
80 disable_telemetry: true,
81 ..self
82 }
83 }
84}
85impl Image for Azurite {
86 fn name(&self) -> &str {
87 NAME
88 }
89
90 fn tag(&self) -> &str {
91 TAG
92 }
93
94 fn ready_conditions(&self) -> Vec<WaitFor> {
95 vec![WaitFor::message_on_stdout(
96 "Azurite Table service is successfully listening at http://0.0.0.0:10002",
97 )]
98 }
99
100 fn env_vars(
101 &self,
102 ) -> impl IntoIterator<Item = (impl Into<Cow<'_, str>>, impl Into<Cow<'_, str>>)> {
103 &self.env_vars
104 }
105
106 fn cmd(&self) -> impl IntoIterator<Item = impl Into<Cow<'_, str>>> {
107 let mut cmd = vec![
108 String::from("azurite"),
109 String::from("--blobHost"),
110 String::from("0.0.0.0"),
111 String::from("--queueHost"),
112 String::from("0.0.0.0"),
113 String::from("--tableHost"),
114 String::from("0.0.0.0"),
115 ];
116 if self.loose {
117 cmd.push(String::from("--loose"));
118 }
119 if self.skip_api_version_check {
120 cmd.push(String::from("--skipApiVersionCheck"));
121 }
122 if self.disable_telemetry {
123 cmd.push(String::from("--disableTelemetry"));
124 }
125 cmd
126 }
127
128 fn expose_ports(&self) -> &[ContainerPort] {
129 &[BLOB_PORT, QUEUE_PORT, TABLE_PORT]
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use azure_storage::{prelude::*, CloudLocation};
136 use azure_storage_blobs::prelude::*;
137 use base64::{prelude::BASE64_STANDARD, Engine};
138
139 use crate::azurite::{Azurite, BLOB_PORT};
140
141 #[tokio::test]
142 async fn starts_with_async_runner() -> Result<(), Box<dyn std::error::Error + 'static>> {
143 use testcontainers::runners::AsyncRunner;
144 let azurite = Azurite::default();
145 azurite.start().await?;
146 Ok(())
147 }
148
149 #[test]
150 fn starts_with_sync_runner() -> Result<(), Box<dyn std::error::Error + 'static>> {
151 use testcontainers::runners::SyncRunner;
152 let azurite = Azurite::default();
153 azurite.start()?;
154 Ok(())
155 }
156
157 #[test]
158 fn starts_with_loose() -> Result<(), Box<dyn std::error::Error + 'static>> {
159 use testcontainers::runners::SyncRunner;
160 let azurite = Azurite::default().with_loose();
161 azurite.start()?;
162 Ok(())
163 }
164
165 #[test]
166 fn starts_with_with_skip_api_version_check() -> Result<(), Box<dyn std::error::Error + 'static>>
167 {
168 use testcontainers::runners::SyncRunner;
169 let azurite = Azurite::default().with_skip_api_version_check();
170 azurite.start()?;
171 Ok(())
172 }
173
174 #[tokio::test]
175 async fn starts_with_accounts() -> Result<(), Box<dyn std::error::Error + 'static>> {
176 use testcontainers::runners::AsyncRunner;
177
178 let data = b"key1";
179 let account_key = BASE64_STANDARD.encode(data);
180
181 let account_name = "account1";
182 let container = Azurite::default()
183 .with_accounts(format!("{account_name}:{account_key};"))
184 .start()
185 .await?;
186
187 ClientBuilder::with_location(
188 CloudLocation::Custom {
189 account: account_name.to_string(),
190 uri: format!(
191 "http://127.0.0.1:{}/{}",
192 container.get_host_port_ipv4(BLOB_PORT).await?,
193 account_name
194 ),
195 },
196 StorageCredentials::access_key(account_name, account_key),
197 )
198 .container_client("container-name")
199 .create()
200 .await?;
201
202 Ok(())
203 }
204}