From 8f3e5f3ca72743d06027786c4a61a821711225ce Mon Sep 17 00:00:00 2001 From: Lzzzt <101313294+Lzzzzzt@users.noreply.github.com> Date: Mon, 26 Aug 2024 10:57:51 +0800 Subject: [PATCH] test: add unitest for oss (#647) Signed-off-by: lzzzt --- Cargo.lock | 776 ++---------------- Cargo.toml | 10 +- dragonfly-client-backend/Cargo.toml | 2 +- dragonfly-client-backend/src/http.rs | 63 +- dragonfly-client-backend/src/lib.rs | 1 + .../src/object_storage.rs | 173 +++- 6 files changed, 274 insertions(+), 751 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index abf9ae81..95fcefe2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -137,15 +137,6 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" -[[package]] -name = "ascii-canvas" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" -dependencies = [ - "term", -] - [[package]] name = "asn1-rs" version = "0.5.2" @@ -195,199 +186,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "async-attributes" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" -dependencies = [ - "quote", - "syn 1.0.109", -] - -[[package]] -name = "async-channel" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" -dependencies = [ - "concurrent-queue", - "event-listener 2.5.3", - "futures-core", -] - -[[package]] -name = "async-channel" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "136d4d23bcc79e27423727b36823d86233aad06dfea531837b038394d11e9928" -dependencies = [ - "concurrent-queue", - "event-listener 5.3.0", - "event-listener-strategy 0.5.1", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-executor" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b10202063978b3351199d68f8b22c4e47e4b1b822f8d43fd862d5ea8c006b29a" -dependencies = [ - "async-task", - "concurrent-queue", - "fastrand 2.0.1", - "futures-lite 2.3.0", - "slab", -] - -[[package]] -name = "async-global-executor" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" -dependencies = [ - "async-channel 2.2.1", - "async-executor", - "async-io 2.3.2", - "async-lock 3.3.0", - "blocking", - "futures-lite 2.3.0", - "once_cell", -] - -[[package]] -name = "async-io" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" -dependencies = [ - "async-lock 2.8.0", - "autocfg", - "cfg-if", - "concurrent-queue", - "futures-lite 1.13.0", - "log", - "parking", - "polling 2.8.0", - "rustix 0.37.27", - "slab", - "socket2 0.4.10", - "waker-fn", -] - -[[package]] -name = "async-io" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcccb0f599cfa2f8ace422d3555572f47424da5648a4382a9dd0310ff8210884" -dependencies = [ - "async-lock 3.3.0", - "cfg-if", - "concurrent-queue", - "futures-io", - "futures-lite 2.3.0", - "parking", - "polling 3.5.0", - "rustix 0.38.31", - "slab", - "tracing", - "windows-sys 0.52.0", -] - -[[package]] -name = "async-lock" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" -dependencies = [ - "event-listener 2.5.3", -] - -[[package]] -name = "async-lock" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" -dependencies = [ - "event-listener 4.0.3", - "event-listener-strategy 0.4.0", - "pin-project-lite", -] - -[[package]] -name = "async-object-pool" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aeb901c30ebc2fc4ab46395bbfbdba9542c16559d853645d75190c3056caf3bc" -dependencies = [ - "async-std", -] - -[[package]] -name = "async-process" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea6438ba0a08d81529c69b36700fa2f95837bfe3e776ab39cde9c14d9149da88" -dependencies = [ - "async-io 1.13.0", - "async-lock 2.8.0", - "async-signal", - "blocking", - "cfg-if", - "event-listener 3.1.0", - "futures-lite 1.13.0", - "rustix 0.38.31", - "windows-sys 0.48.0", -] - -[[package]] -name = "async-signal" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5" -dependencies = [ - "async-io 2.3.2", - "async-lock 2.8.0", - "atomic-waker", - "cfg-if", - "futures-core", - "futures-io", - "rustix 0.38.31", - "signal-hook-registry", - "slab", - "windows-sys 0.48.0", -] - -[[package]] -name = "async-std" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" -dependencies = [ - "async-attributes", - "async-channel 1.9.0", - "async-global-executor", - "async-io 1.13.0", - "async-lock 2.8.0", - "async-process", - "crossbeam-utils", - "futures-channel", - "futures-core", - "futures-io", - "futures-lite 1.13.0", - "gloo-timers", - "kv-log-macro", - "log", - "memchr", - "once_cell", - "pin-project-lite", - "pin-utils", - "slab", - "wasm-bindgen-futures", -] - [[package]] name = "async-stream" version = "0.3.5" @@ -410,12 +208,6 @@ dependencies = [ "syn 2.0.48", ] -[[package]] -name = "async-task" -version = "4.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" - [[package]] name = "async-trait" version = "0.1.77" @@ -427,12 +219,6 @@ dependencies = [ "syn 2.0.48", ] -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - [[package]] name = "autocfg" version = "1.1.0" @@ -492,7 +278,7 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d67782c3f868daa71d3533538e98a8e13713231969def7536e8039606fc46bf0" dependencies = [ - "fastrand 2.0.1", + "fastrand", "futures-core", "pin-project", "tokio", @@ -537,17 +323,6 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" -[[package]] -name = "basic-cookies" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67bd8fd42c16bdb08688243dc5f0cc117a3ca9efeeaba3a345a18a6159ad96f7" -dependencies = [ - "lalrpop", - "lalrpop-util", - "regex", -] - [[package]] name = "bincode" version = "1.3.3" @@ -577,21 +352,6 @@ dependencies = [ "syn 2.0.48", ] -[[package]] -name = "bit-set" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - [[package]] name = "bitflags" version = "1.3.2" @@ -635,22 +395,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "blocking" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" -dependencies = [ - "async-channel 2.2.1", - "async-lock 3.3.0", - "async-task", - "fastrand 2.0.1", - "futures-io", - "futures-lite 2.3.0", - "piper", - "tracing", -] - [[package]] name = "bumpalo" version = "3.14.0" @@ -827,15 +571,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" -[[package]] -name = "concurrent-queue" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" -dependencies = [ - "crossbeam-utils", -] - [[package]] name = "console" version = "0.15.8" @@ -1016,6 +751,24 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" +[[package]] +name = "deadpool" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb84100978c1c7b37f09ed3ce3e5f843af02c2a2c431bae5b19230dad2c1b490" +dependencies = [ + "async-trait", + "deadpool-runtime", + "num_cpus", + "tokio", +] + +[[package]] +name = "deadpool-runtime" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "092966b41edc516079bdf31ec78a2e0588d1d0c08f78b91d8307215928642b2b" + [[package]] name = "debugid" version = "0.8.0" @@ -1071,27 +824,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "dirs-next" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" -dependencies = [ - "cfg-if", - "dirs-sys-next", -] - -[[package]] -name = "dirs-sys-next" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - [[package]] name = "displaydoc" version = "0.2.4" @@ -1205,7 +937,6 @@ dependencies = [ "dragonfly-client-core", "dragonfly-client-util", "futures", - "httpmock", "libloading", "opendal", "percent-encoding", @@ -1217,6 +948,7 @@ dependencies = [ "tonic", "tracing", "url", + "wiremock", ] [[package]] @@ -1327,15 +1059,6 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" -[[package]] -name = "ena" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c533630cf40e9caa44bd91aadc88a75d75a4c3a12b4cfde353cbed41daa1e1f1" -dependencies = [ - "log", -] - [[package]] name = "encode_unicode" version = "0.3.6" @@ -1376,74 +1099,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - -[[package]] -name = "event-listener" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d93877bcde0eb80ca09131a08d23f0a5c18a620b01db137dba666d18cd9b30c2" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener" -version = "4.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d9944b8ca13534cdfb2800775f8dd4902ff3fc75a50101466decadfdf322a24" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener-strategy" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" -dependencies = [ - "event-listener 4.0.3", - "pin-project-lite", -] - -[[package]] -name = "event-listener-strategy" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "332f51cb23d20b0de8458b86580878211da09bcd4503cb579c225b3d124cabb3" -dependencies = [ - "event-listener 5.3.0", - "pin-project-lite", -] - -[[package]] -name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] - [[package]] name = "fastrand" version = "2.0.1" @@ -1588,34 +1243,6 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" -[[package]] -name = "futures-lite" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" -dependencies = [ - "fastrand 1.9.0", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] - -[[package]] -name = "futures-lite" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" -dependencies = [ - "fastrand 2.0.1", - "futures-core", - "futures-io", - "parking", - "pin-project-lite", -] - [[package]] name = "futures-macro" version = "0.3.30" @@ -1692,18 +1319,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" -[[package]] -name = "gloo-timers" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", -] - [[package]] name = "h2" version = "0.3.26" @@ -1927,34 +1542,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" -[[package]] -name = "httpmock" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08ec9586ee0910472dec1a1f0f8acf52f0fdde93aea74d70d4a3107b4be0fd5b" -dependencies = [ - "assert-json-diff", - "async-object-pool", - "async-std", - "async-trait", - "base64 0.21.7", - "basic-cookies", - "crossbeam-utils", - "form_urlencoded", - "futures-util", - "hyper 0.14.28", - "lazy_static", - "levenshtein", - "log", - "regex", - "serde", - "serde_json", - "serde_regex", - "similar", - "tokio", - "url", -] - [[package]] name = "humantime" version = "2.1.0" @@ -1988,7 +1575,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.5.5", + "socket2", "tokio", "tower-service", "tracing", @@ -2077,7 +1664,7 @@ dependencies = [ "http-body 1.0.0", "hyper 1.4.1", "pin-project-lite", - "socket2 0.5.5", + "socket2", "tokio", "tower", "tower-service", @@ -2215,17 +1802,6 @@ version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767" -[[package]] -name = "io-lifetimes" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi", - "libc", - "windows-sys 0.48.0", -] - [[package]] name = "ipnet" version = "2.9.0" @@ -2317,46 +1893,6 @@ dependencies = [ "simple_asn1", ] -[[package]] -name = "kv-log-macro" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" -dependencies = [ - "log", -] - -[[package]] -name = "lalrpop" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cb077ad656299f160924eb2912aa147d7339ea7d69e1b5517326fdcec3c1ca" -dependencies = [ - "ascii-canvas", - "bit-set", - "ena", - "itertools 0.11.0", - "lalrpop-util", - "petgraph", - "pico-args", - "regex", - "regex-syntax 0.8.2", - "string_cache", - "term", - "tiny-keccak", - "unicode-xid", - "walkdir", -] - -[[package]] -name = "lalrpop-util" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" -dependencies = [ - "regex-automata 0.4.5", -] - [[package]] name = "lazy_static" version = "1.5.0" @@ -2383,12 +1919,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "levenshtein" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760" - [[package]] name = "libc" version = "0.2.155" @@ -2422,16 +1952,6 @@ dependencies = [ "redox_syscall", ] -[[package]] -name = "libredox" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" -dependencies = [ - "bitflags 2.4.2", - "libc", -] - [[package]] name = "librocksdb-sys" version = "0.16.0+8.10.0" @@ -2459,12 +1979,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "linux-raw-sys" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" - [[package]] name = "linux-raw-sys" version = "0.4.13" @@ -2498,9 +2012,6 @@ name = "log" version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" -dependencies = [ - "value-bag", -] [[package]] name = "lz4-sys" @@ -2675,12 +2186,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "new_debug_unreachable" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" - [[package]] name = "nix" version = "0.26.4" @@ -2878,9 +2383,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "opendal" -version = "0.47.3" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cac4826fe3d5482a49b92955b0f6b06ce45b46ec84484176588209bfbf996870" +checksum = "615d41187deea0ea7fab5b48e9afef6ae8fc742fdcfa248846ee3d92ff71e986" dependencies = [ "anyhow", "async-trait", @@ -2897,7 +2402,7 @@ dependencies = [ "md-5", "once_cell", "percent-encoding", - "quick-xml 0.31.0", + "quick-xml 0.36.1", "reqsign", "reqwest", "serde", @@ -3071,12 +2576,6 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "parking" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" - [[package]] name = "parking_lot" version = "0.12.1" @@ -3169,21 +2668,6 @@ dependencies = [ "indexmap 2.2.1", ] -[[package]] -name = "phf_shared" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" -dependencies = [ - "siphasher", -] - -[[package]] -name = "pico-args" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" - [[package]] name = "pin-project" version = "1.1.4" @@ -3216,17 +2700,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "piper" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" -dependencies = [ - "atomic-waker", - "fastrand 2.0.1", - "futures-io", -] - [[package]] name = "pkcs1" version = "0.7.5" @@ -3271,36 +2744,6 @@ version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb" -[[package]] -name = "polling" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" -dependencies = [ - "autocfg", - "bitflags 1.3.2", - "cfg-if", - "concurrent-queue", - "libc", - "log", - "pin-project-lite", - "windows-sys 0.48.0", -] - -[[package]] -name = "polling" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24f040dee2588b4963afb4e420540439d126f73fdacf4a9c486a96d840bac3c9" -dependencies = [ - "cfg-if", - "concurrent-queue", - "pin-project-lite", - "rustix 0.38.31", - "tracing", - "windows-sys 0.52.0", -] - [[package]] name = "portable-atomic" version = "1.6.0" @@ -3355,12 +2798,6 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" -[[package]] -name = "precomputed-hash" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" - [[package]] name = "prettyplease" version = "0.2.17" @@ -3414,7 +2851,7 @@ dependencies = [ "hex", "lazy_static", "procfs-core", - "rustix 0.38.31", + "rustix", ] [[package]] @@ -3602,9 +3039,19 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.31.0" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" +checksum = "86e446ed58cef1bbfe847bc2fda0e2e4ea9f0e57b90c507d4781292590d72a4e" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "quick-xml" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96a05e2e8efddfa51a84ca47cec303fac86c8541b686d37cac5efc0e094417bc" dependencies = [ "memchr", "serde", @@ -3734,17 +3181,6 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20145670ba436b55d91fc92d25e71160fbfbdd57831631c8d7d36377a476f1cb" -[[package]] -name = "redox_users" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" -dependencies = [ - "getrandom", - "libredox 0.1.3", - "thiserror", -] - [[package]] name = "regex" version = "1.10.6" @@ -3800,9 +3236,9 @@ dependencies = [ [[package]] name = "reqsign" -version = "0.15.2" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70fe66d4cd0b5ed9b1abbfe639bf6baeaaf509f7da2d51b31111ba945be59286" +checksum = "03dd4ba7c3901dd43e6b8c7446a760d45bc1ea4301002e1a6fa48f97c3a796fa" dependencies = [ "anyhow", "async-trait", @@ -3818,7 +3254,7 @@ dependencies = [ "log", "once_cell", "percent-encoding", - "quick-xml 0.31.0", + "quick-xml 0.35.0", "rand 0.8.5", "reqwest", "rsa", @@ -3997,20 +3433,6 @@ dependencies = [ "nom", ] -[[package]] -name = "rustix" -version = "0.37.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" -dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys 0.3.8", - "windows-sys 0.48.0", -] - [[package]] name = "rustix" version = "0.38.31" @@ -4020,7 +3442,7 @@ dependencies = [ "bitflags 2.4.2", "errno", "libc", - "linux-raw-sys 0.4.13", + "linux-raw-sys", "windows-sys 0.52.0", ] @@ -4108,15 +3530,6 @@ dependencies = [ "cipher", ] -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - [[package]] name = "schannel" version = "0.1.23" @@ -4310,12 +3723,6 @@ dependencies = [ "rand_core 0.6.4", ] -[[package]] -name = "similar" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640" - [[package]] name = "simple_asn1" version = "0.6.2" @@ -4349,16 +3756,6 @@ version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" -[[package]] -name = "socket2" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "socket2" version = "0.5.5" @@ -4403,19 +3800,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9091b6114800a5f2141aee1d1b9d6ca3592ac062dc5decb3764ec5895a47b4eb" -[[package]] -name = "string_cache" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" -dependencies = [ - "new_debug_unreachable", - "once_cell", - "parking_lot", - "phf_shared", - "precomputed-hash", -] - [[package]] name = "strsim" version = "0.11.0" @@ -4573,23 +3957,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" dependencies = [ "cfg-if", - "fastrand 2.0.1", + "fastrand", "once_cell", - "rustix 0.38.31", + "rustix", "windows-sys 0.59.0", ] -[[package]] -name = "term" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" -dependencies = [ - "dirs-next", - "rustversion", - "winapi", -] - [[package]] name = "termion" version = "4.0.2" @@ -4597,7 +3970,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ccce68e518d1173e80876edd54760b60b792750d0cab6444a79101c6ea03848" dependencies = [ "libc", - "libredox 0.0.2", + "libredox", "numtoa", "redox_termios", ] @@ -4753,7 +4126,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.5", + "socket2", "tokio-macros", "windows-sys 0.52.0", ] @@ -4885,7 +4258,7 @@ dependencies = [ "percent-encoding", "pin-project", "prost 0.13.1", - "socket2 0.5.5", + "socket2", "tokio", "tokio-stream", "tower", @@ -5269,12 +4642,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" -[[package]] -name = "value-bag" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74797339c3b98616c009c7c3eb53a0ce41e85c8ec66bd3db96ed132d20cfdee8" - [[package]] name = "vcpkg" version = "0.2.15" @@ -5287,22 +4654,6 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" -[[package]] -name = "waker-fn" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" - -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - [[package]] name = "want" version = "0.3.1" @@ -5463,15 +4814,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -[[package]] -name = "winapi-util" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" -dependencies = [ - "winapi", -] - [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -5664,6 +5006,30 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "wiremock" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a59f8ae78a4737fb724f20106fb35ccb7cfe61ff335665d3042b3aa98e34717" +dependencies = [ + "assert-json-diff", + "async-trait", + "base64 0.21.7", + "deadpool", + "futures", + "http 1.1.0", + "http-body-util", + "hyper 1.4.1", + "hyper-util", + "log", + "once_cell", + "regex", + "serde", + "serde_json", + "tokio", + "url", +] + [[package]] name = "x509-parser" version = "0.15.1" diff --git a/Cargo.toml b/Cargo.toml index 03efeb9c..32ce219f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ members = [ "dragonfly-client-init", "dragonfly-client-storage", "dragonfly-client-util", - "dragonfly-client-backend/examples/plugin" + "dragonfly-client-backend/examples/plugin", ] [workspace.package] @@ -70,10 +70,10 @@ humantime = "2.1.0" prost-wkt-types = "0.6" chrono = { version = "0.4.35", features = ["serde", "clock"] } openssl = { version = "0.10", features = ["vendored"] } -opendal = { version = "0.47.3", features = [ - "services-s3", +opendal = { version = "0.48.0", features = [ + "services-s3", "services-azblob", - "services-gcs", + "services-gcs", "services-oss", "services-obs", "services-cos", @@ -83,7 +83,7 @@ anyhow = "1.0.86" toml_edit = "0.22.14" toml = "0.8.19" base16ct = { version = "0.2", features = ["alloc"] } -bytesize = {version = "1.2.0", features = ["serde"]} +bytesize = { version = "1.2.0", features = ["serde"] } bytesize-serde = "0.2.1" percent-encoding = "2.3.1" diff --git a/dragonfly-client-backend/Cargo.toml b/dragonfly-client-backend/Cargo.toml index 081f5f64..9dfb7e2e 100644 --- a/dragonfly-client-backend/Cargo.toml +++ b/dragonfly-client-backend/Cargo.toml @@ -27,4 +27,4 @@ futures = "0.3.28" libloading = "0.8.5" [dev-dependencies] -httpmock = "0.7.0" +wiremock = "0.6.1" diff --git a/dragonfly-client-backend/src/http.rs b/dragonfly-client-backend/src/http.rs index 6a9f45d9..32e1e2ae 100644 --- a/dragonfly-client-backend/src/http.rs +++ b/dragonfly-client-backend/src/http.rs @@ -177,24 +177,30 @@ impl Default for HTTP { #[cfg(test)] mod tests { use crate::{http, Backend, GetRequest, HeadRequest}; - use httpmock::{Method, MockServer}; use reqwest::{header::HeaderMap, StatusCode}; + use wiremock::{ + matchers::{method, path}, + Mock, ResponseTemplate, + }; #[tokio::test] async fn should_get_head_response() { - let server = MockServer::start(); - server.mock(|when, then| { - when.method(Method::GET).path("/head"); - then.status(200) - .header("content-type", "text/html; charset=UTF-8") - .body(""); - }); + let server = wiremock::MockServer::start().await; + + Mock::given(method("GET")) + .and(path("/head")) + .respond_with( + ResponseTemplate::new(200) + .insert_header("Content-Type", "text/html; charset=UTF-8"), + ) + .mount(&server) + .await; let http_backend = http::HTTP::new("http"); let resp = http_backend .head(HeadRequest { task_id: "test".to_string(), - url: server.url("/head"), + url: format!("{}/head", server.uri()), http_header: Some(HeaderMap::new()), timeout: std::time::Duration::from_secs(5), client_certs: None, @@ -208,19 +214,22 @@ mod tests { #[tokio::test] async fn should_return_error_response_when_head_notexists() { - let server = MockServer::start(); - server.mock(|when, then| { - when.method(Method::GET).path("/head"); - then.status(200) - .header("content-type", "text/html; charset=UTF-8") - .body(""); - }); + let server = wiremock::MockServer::start().await; + + Mock::given(method("GET")) + .and(path("/head")) + .respond_with( + ResponseTemplate::new(200) + .insert_header("Content-Type", "text/html; charset=UTF-8"), + ) + .mount(&server) + .await; let http_backend = http::HTTP::new("http"); let resp = http_backend .head(HeadRequest { task_id: "test".to_string(), - url: server.url("/head"), + url: format!("{}/head", server.uri()), http_header: None, timeout: std::time::Duration::from_secs(5), client_certs: None, @@ -233,20 +242,24 @@ mod tests { #[tokio::test] async fn should_get_response() { - let server = MockServer::start(); - server.mock(|when, then| { - when.method(Method::GET).path("/get"); - then.status(200) - .header("content-type", "text/html; charset=UTF-8") - .body("OK"); - }); + let server = wiremock::MockServer::start().await; + + Mock::given(method("GET")) + .and(path("/get")) + .respond_with( + ResponseTemplate::new(200) + .insert_header("Content-Type", "text/html; charset=UTF-8") + .set_body_string("OK"), + ) + .mount(&server) + .await; let http_backend = http::HTTP::new("http"); let mut resp = http_backend .get(GetRequest { task_id: "test".to_string(), piece_id: "test".to_string(), - url: server.url("/get"), + url: format!("{}/get", server.uri()), range: None, http_header: Some(HeaderMap::new()), timeout: std::time::Duration::from_secs(5), diff --git a/dragonfly-client-backend/src/lib.rs b/dragonfly-client-backend/src/lib.rs index c5b12515..075556e4 100644 --- a/dragonfly-client-backend/src/lib.rs +++ b/dragonfly-client-backend/src/lib.rs @@ -60,6 +60,7 @@ pub struct HeadRequest { } // HeadResponse is the head response for backend. +#[derive(Debug)] pub struct HeadResponse { // success is the success of the response. pub success: bool, diff --git a/dragonfly-client-backend/src/object_storage.rs b/dragonfly-client-backend/src/object_storage.rs index b2acbe34..f5e3c9b6 100644 --- a/dragonfly-client-backend/src/object_storage.rs +++ b/dragonfly-client-backend/src/object_storage.rs @@ -202,7 +202,7 @@ impl ObjectStorage { // Initialize the S3 operator with the object storage. let mut builder = opendal::services::S3::default(); - builder + builder = builder .access_key_id(&object_storage.access_key_id) .secret_access_key(&object_storage.access_key_secret) .http_client(HttpClient::with(client)) @@ -210,17 +210,17 @@ impl ObjectStorage { // Configure the region and endpoint if they are provided. if let Some(region) = object_storage.region.as_deref() { - builder.region(region); + builder = builder.region(region); } // Configure the endpoint if it is provided. if let Some(endpoint) = object_storage.endpoint.as_deref() { - builder.endpoint(endpoint); + builder = builder.endpoint(endpoint); } // Configure the session token if it is provided. if let Some(session_token) = object_storage.session_token.as_deref() { - builder.security_token(session_token); + builder = builder.session_token(session_token); } Ok(Operator::new(builder)?.finish()) @@ -248,13 +248,13 @@ impl ObjectStorage { // Initialize the GCS operator with the object storage. let mut builder = opendal::services::Gcs::default(); - builder + builder = builder .http_client(HttpClient::with(client)) .bucket(&parsed_url.bucket); // Configure the region and endpoint if they are provided. if let Some(credential) = object_storage.credential.as_deref() { - builder.credential(credential); + builder = builder.credential(credential); } else { error!("need credential"); return Err(ClientError::BackendError(BackendError { @@ -266,7 +266,7 @@ impl ObjectStorage { // Configure the predefined ACL if it is provided. if let Some(predefined_acl) = object_storage.predefined_acl.as_deref() { - builder.predefined_acl(predefined_acl); + builder = builder.predefined_acl(predefined_acl); } Ok(Operator::new(builder)?.finish()) @@ -294,7 +294,7 @@ impl ObjectStorage { // Initialize the ABS operator with the object storage. let mut builder = opendal::services::Azblob::default(); - builder + builder = builder .account_name(&object_storage.access_key_id) .account_key(&object_storage.access_key_secret) .http_client(HttpClient::with(client)) @@ -302,7 +302,7 @@ impl ObjectStorage { // Configure the endpoint if it is provided. if let Some(endpoint) = object_storage.endpoint.as_deref() { - builder.endpoint(endpoint); + builder = builder.endpoint(endpoint); } Ok(Operator::new(builder)?.finish()) @@ -330,7 +330,7 @@ impl ObjectStorage { // Initialize the OSS operator with the object storage. let mut builder = opendal::services::Oss::default(); - builder + builder = builder .access_key_id(&object_storage.access_key_id) .access_key_secret(&object_storage.access_key_secret) .http_client(HttpClient::with(client)) @@ -339,7 +339,14 @@ impl ObjectStorage { // Configure the endpoint if provided. if let Some(endpoint) = object_storage.endpoint { - builder.endpoint(&endpoint); + builder = builder.endpoint(&endpoint); + } else { + error!("need endpoint"); + return Err(ClientError::BackendError(BackendError { + message: "need endpoint".to_string(), + status_code: None, + header: None, + })); } Ok(Operator::new(builder)?.finish()) @@ -367,7 +374,7 @@ impl ObjectStorage { // Initialize the OBS operator with the object storage. let mut builder = opendal::services::Obs::default(); - builder + builder = builder .access_key_id(&object_storage.access_key_id) .secret_access_key(&object_storage.access_key_secret) .http_client(HttpClient::with(client)) @@ -375,7 +382,7 @@ impl ObjectStorage { // Configure the endpoint if provided. if let Some(endpoint) = object_storage.endpoint { - builder.endpoint(&endpoint); + builder = builder.endpoint(&endpoint); } Ok(Operator::new(builder)?.finish()) @@ -403,7 +410,7 @@ impl ObjectStorage { // Initialize the COS operator with the object storage. let mut builder = opendal::services::Cos::default(); - builder + builder = builder .secret_id(&object_storage.access_key_id) .secret_key(&object_storage.access_key_secret) .http_client(HttpClient::with(client)) @@ -411,7 +418,7 @@ impl ObjectStorage { // Configure the endpoint if provided. if let Some(endpoint) = object_storage.endpoint { - builder.endpoint(&endpoint); + builder = builder.endpoint(&endpoint); } Ok(Operator::new(builder)?.finish()) @@ -588,3 +595,139 @@ impl crate::Backend for ObjectStorage { }) } } + +#[cfg(test)] +mod tests { + use super::*; + + use dragonfly_client_core::Error; + + #[test] + fn should_get_parsed_url() { + let file_key = "test-bucket/file"; + let dir_key = "test-bucket/path/to/dir/"; + let schemes = vec![ + Scheme::OBS, + Scheme::S3, + Scheme::ABS, + Scheme::OSS, + Scheme::COS, + Scheme::GCS, + ]; + + // Test each scheme for both file and directory URLs. + for scheme in schemes { + let file_url = format!("{}://{}", scheme, file_key); + + let url: Url = file_url.parse().unwrap(); + let parsed_url: ParsedURL = url.try_into().unwrap(); + + // Assert that the file URL is parsed correctly. + assert!(!parsed_url.is_dir()); + assert_eq!(parsed_url.bucket, "test-bucket"); + assert_eq!(parsed_url.key, "file"); + assert_eq!(parsed_url.scheme, scheme); + + let dir_url = format!("{}://{}", scheme, dir_key); + + let url: Url = dir_url.parse().unwrap(); + let parsed_url: ParsedURL = url.try_into().unwrap(); + + // Assert that the directory URL is parsed correctly. + assert!(parsed_url.is_dir()); + assert_eq!(parsed_url.bucket, "test-bucket"); + assert_eq!(parsed_url.key, "path/to/dir/"); + assert_eq!(parsed_url.scheme, scheme); + } + } + + #[test] + fn should_return_error_when_scheme_not_valid() { + let url: Url = "github://test-bucket/file".parse().unwrap(); + + let result = TryInto::::try_into(url); + + // Assert that an invalid scheme returns an error. + assert!(result.is_err()); + assert!(matches!(result.unwrap_err(), Error::InvalidURI(..))); + } + + #[test] + fn should_return_error_when_bucket_not_valid() { + let schemes = vec![ + Scheme::OBS, + Scheme::S3, + Scheme::ABS, + Scheme::OSS, + Scheme::COS, + Scheme::GCS, + ]; + + for scheme in schemes { + let url: Url = format!("{}:///file", scheme).parse().unwrap(); + + let result = TryInto::::try_into(url); + + // Assert that an invalid bucket returns an error. + assert!(result.is_err()); + assert!(matches!(result.unwrap_err(), Error::InvalidURI(..))); + } + } + + #[test] + fn should_get_oss_operator() { + let url: Url = "oss://test-bucket/file".parse().unwrap(); + let parsed_url: ParsedURL = url.try_into().unwrap(); + + let object_storage = dragonfly_api::common::v2::ObjectStorage { + endpoint: Some("test-endpoint.local".into()), + access_key_id: "access-key-id".into(), + access_key_secret: "access-key-secret".into(), + ..Default::default() + }; + + let result = ObjectStorage::new(Scheme::OSS).oss_operator( + &parsed_url, + Some(object_storage), + Duration::from_secs(3), + ); + + // Assert that the OSS operator is successfully created. + assert!(result.is_ok()); + } + + #[test] + fn should_return_error_when_oss_aksk_not_provided() { + let url: Url = "oss://test-bucket/file".parse().unwrap(); + let parsed_url: ParsedURL = url.try_into().unwrap(); + + let result = + ObjectStorage::new(Scheme::OSS).oss_operator(&parsed_url, None, Duration::from_secs(3)); + + // Assert that missing access keys return an error. + assert!(result.is_err()); + assert!(matches!(result.unwrap_err(), Error::BackendError(..))); + } + + #[test] + fn should_return_error_when_oss_endpoint_not_provided() { + let url: Url = "oss://test-bucket/file".parse().unwrap(); + let parsed_url: ParsedURL = url.try_into().unwrap(); + + let object_storage = dragonfly_api::common::v2::ObjectStorage { + access_key_id: "access-key-id".into(), + access_key_secret: "access-key-secret".into(), + ..Default::default() + }; + + let result = ObjectStorage::new(Scheme::OSS).oss_operator( + &parsed_url, + Some(object_storage), + Duration::from_secs(3), + ); + + // Assert that missing endpoint returns an error. + assert!(result.is_err()); + assert!(matches!(result.unwrap_err(), Error::BackendError(..))); + } +}