use std::collections::HashMap;
use std::env;
use std::sync::LazyLock;
use opendal_core::Operator;
use opendal_core::Result;
use opendal_core::layers::CapabilityOverrideLayer;
use opendal_layer_logging::LoggingLayer;
use opendal_layer_retry::RetryLayer;
use opendal_layer_timeout::TimeoutLayer;
use sha2::Digest;
use sha2::Sha256;
const OPENDAL_TEST_CAPABILITY_OVERRIDES: &str = "OPENDAL_TEST_CAPABILITY_OVERRIDES";
const OPENDAL_TEST_UNSET_VALUE: &str = "__OPENDAL_TEST_UNSET__";
pub(crate) fn sha256_digest(data: impl AsRef<[u8]>) -> String {
use std::fmt::Write;
let digest = Sha256::digest(data);
let mut output = String::with_capacity(digest.len() * 2);
for byte in digest {
write!(&mut output, "{byte:02x}").expect("writing to String must succeed");
}
output
}
pub static TEST_RUNTIME: LazyLock<tokio::runtime::Runtime> = LazyLock::new(|| {
tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap()
});
fn collect_config(
prefix: &str,
vars: impl IntoIterator<Item = (String, String)>,
) -> HashMap<String, String> {
vars.into_iter()
.filter_map(|(k, v)| {
if v == OPENDAL_TEST_UNSET_VALUE {
return None;
}
k.to_lowercase()
.strip_prefix(prefix)
.map(|k| (k.to_string(), v))
})
.collect()
}
pub fn init_test_service() -> Result<Option<Operator>> {
let _ = dotenvy::dotenv();
let scheme = if let Ok(v) = env::var("OPENDAL_TEST") {
v
} else {
return Ok(None);
};
let prefix = {
let scheme_key = scheme.replace('-', "_");
format!("opendal_{scheme_key}_")
};
let mut cfg = collect_config(&prefix, env::vars());
let disable_random_root = env::var("OPENDAL_DISABLE_RANDOM_ROOT").unwrap_or_default() == "true";
if !disable_random_root {
let root = format!(
"{}{}/",
cfg.get("root").cloned().unwrap_or_else(|| "/".to_string()),
uuid::Uuid::new_v4()
);
cfg.insert("root".to_string(), root);
}
let scheme = scheme.replace('_', "-");
let mut op = Operator::via_iter(scheme, cfg).expect("must succeed");
if let Ok(overrides) = env::var(OPENDAL_TEST_CAPABILITY_OVERRIDES)
&& overrides != OPENDAL_TEST_UNSET_VALUE
{
op = op.layer(CapabilityOverrideLayer::from_overrides(&overrides)?);
}
let op = op
.layer(LoggingLayer::default())
.layer(TimeoutLayer::new())
.layer(RetryLayer::new().with_max_times(4));
Ok(Some(op))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_collect_config_skips_unset_values() {
let cfg = collect_config(
"opendal_s3_",
[
(
"OPENDAL_S3_ENDPOINT".to_string(),
"http://localhost".to_string(),
),
(
"OPENDAL_S3_ALLOW_ANONYMOUS".to_string(),
OPENDAL_TEST_UNSET_VALUE.to_string(),
),
("OPENDAL_S3_PASSWORD".to_string(), String::new()),
("OPENDAL_GCS_BUCKET".to_string(), "test".to_string()),
],
);
assert_eq!(
cfg.get("endpoint").map(String::as_str),
Some("http://localhost")
);
assert_eq!(cfg.get("password").map(String::as_str), Some(""));
assert!(!cfg.contains_key("allow_anonymous"));
assert!(!cfg.contains_key("bucket"));
}
}