Skip to main content

opendal_testkit/
utils.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18use std::collections::HashMap;
19use std::env;
20use std::sync::LazyLock;
21
22use opendal_core::Operator;
23use opendal_core::Result;
24use opendal_core::layers::CapabilityOverrideLayer;
25use opendal_layer_logging::LoggingLayer;
26use opendal_layer_retry::RetryLayer;
27use opendal_layer_timeout::TimeoutLayer;
28use sha2::Digest;
29use sha2::Sha256;
30
31const OPENDAL_TEST_CAPABILITY_OVERRIDES: &str = "OPENDAL_TEST_CAPABILITY_OVERRIDES";
32
33pub(crate) fn sha256_digest(data: impl AsRef<[u8]>) -> String {
34    use std::fmt::Write;
35
36    let digest = Sha256::digest(data);
37    let mut output = String::with_capacity(digest.len() * 2);
38    for byte in digest {
39        write!(&mut output, "{byte:02x}").expect("writing to String must succeed");
40    }
41    output
42}
43
44/// TEST_RUNTIME is the runtime used for running tests.
45pub static TEST_RUNTIME: LazyLock<tokio::runtime::Runtime> = LazyLock::new(|| {
46    tokio::runtime::Builder::new_multi_thread()
47        .enable_all()
48        .build()
49        .unwrap()
50});
51
52/// Init a service with given scheme.
53///
54/// - Load scheme from `OPENDAL_TEST`
55/// - Construct a new Operator with given root.
56/// - Else, returns a `None` to represent no valid config for operator.
57pub fn init_test_service() -> Result<Option<Operator>> {
58    let _ = dotenvy::dotenv();
59
60    let scheme = if let Ok(v) = env::var("OPENDAL_TEST") {
61        v
62    } else {
63        return Ok(None);
64    };
65
66    let prefix = {
67        let scheme_key = scheme.replace('-', "_");
68        format!("opendal_{scheme_key}_")
69    };
70
71    let mut cfg = env::vars()
72        .filter_map(|(k, v)| {
73            k.to_lowercase()
74                .strip_prefix(&prefix)
75                .map(|k| (k.to_string(), v))
76        })
77        .collect::<HashMap<String, String>>();
78
79    // Use random root unless OPENDAL_DISABLE_RANDOM_ROOT is set to true.
80    let disable_random_root = env::var("OPENDAL_DISABLE_RANDOM_ROOT").unwrap_or_default() == "true";
81    if !disable_random_root {
82        let root = format!(
83            "{}{}/",
84            cfg.get("root").cloned().unwrap_or_else(|| "/".to_string()),
85            uuid::Uuid::new_v4()
86        );
87        cfg.insert("root".to_string(), root);
88    }
89
90    // string-based scheme uses a hyphen ('-') as the connector
91    let scheme = scheme.replace('_', "-");
92    let mut op = Operator::via_iter(scheme, cfg).expect("must succeed");
93
94    if let Ok(overrides) = env::var(OPENDAL_TEST_CAPABILITY_OVERRIDES) {
95        op = op.layer(CapabilityOverrideLayer::from_overrides(&overrides)?);
96    }
97
98    let op = op
99        .layer(LoggingLayer::default())
100        .layer(TimeoutLayer::new())
101        .layer(RetryLayer::new().with_max_times(4));
102
103    Ok(Some(op))
104}