diff --git a/tests/suites/conformance/Cargo.lock b/tests/suites/conformance/Cargo.lock index 89d8c0d39..2c6a2eb27 100644 --- a/tests/suites/conformance/Cargo.lock +++ b/tests/suites/conformance/Cargo.lock @@ -2,24 +2,346 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "addr2line" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aho-corasick" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" +dependencies = [ + "memchr", +] + +[[package]] +name = "android_system_properties" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" + +[[package]] +name = "async-stream" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "async-trait" +version = "0.1.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.5", +] + +[[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.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "aws-lc-rs" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce2b2dcc879c3bae0d371e77c99f2238400ef24ec001394befa67b6e543add9e" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f09fae7be8bb3174e05c6afdb34199e6dc0c7c04ba9fa237b1967adfbde27483" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", + "pkg-config", +] + +[[package]] +name = "axum" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90" +dependencies = [ + "axum-core", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "serde_core", + "sync_wrapper", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "sync_wrapper", + "tower-layer", + "tower-service", +] + +[[package]] +name = "backtrace" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-link", +] + +[[package]] +name = "backtrace-ext" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "537beee3be4a18fb023b570f80e3ae28003db9167a751266b259926e25539d50" +dependencies = [ + "backtrace", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" + [[package]] name = "bitflags" version = "2.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06" +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + [[package]] name = "bytes" version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" +[[package]] +name = "cc" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6651c9ed80effdc7db0ff72512157f901af5e3549e341e24b1dd4887d836d838" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + [[package]] name = "cfg-if" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "cfg_aliases" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" + +[[package]] +name = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "cmake" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" +dependencies = [ + "cc", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "data-encoding" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06" + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "displaydoc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.5", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "either" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e9c71c2167ca323c882b99918929403426e2373ea17242ff5653e0d5e1058be" + +[[package]] +name = "equivalent" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00d174d5400e5e8fd687ad1049e2f578285fa914201b1af7e8b112a4546bd826" + [[package]] name = "errno" version = "0.3.14" @@ -27,7 +349,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.61.2", ] [[package]] @@ -36,6 +358,156 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" +[[package]] +name = "find-msvc-tools" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aedcfb3409746eddb02b9e19ebda1c3394f759a152e48ee875a0844d1b955484" + +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + +[[package]] +name = "futures" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a31d2a3fbaaeb2af2368bbdd904aa8e812d3c04a1ee10d3171f52d556e5d0a3" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" + +[[package]] +name = "futures-executor" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "031b47cf1a3c6cc8bc2fc76cd437f521619387907d469316e7c0bc278f1f5432" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed" + +[[package]] +name = "futures-macro" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.5", +] + +[[package]] +name = "futures-sink" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d" + +[[package]] +name = "futures-task" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" + +[[package]] +name = "futures-util" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + [[package]] name = "getrandom" version = "0.3.4" @@ -44,28 +516,450 @@ checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "libc", - "r-efi", + "r-efi 5.3.0", "wasip2", ] +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", +] + +[[package]] +name = "gimli" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" + +[[package]] +name = "glob" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" + +[[package]] +name = "h2" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d29020232d6aa3fb1daca64c1127cf662cf97f254ae16c18c05b8ab635fc118" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "http" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23169fe34a5fbcdd3f3862e78fb9b6fccd5f02a6dc6f732547005d45631ce71c" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c3e324da4c95177d6291d4c8730197c0d1822f8a9766814a4a44fa5ab797c9c" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfa8e654703247911e29c23fbeaa261834bd9bb74efba2f9acddc37bfb127f53" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-native-certs", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-timeout" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" +dependencies = [ + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddc03d96684f9226b8a787cdb71488417b53ab5ea8fdb1dac946cb9431cc8bff" +dependencies = [ + "base64 0.23.1", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "httparse", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb" +dependencies = [ + "displaydoc", + "litemap", + "serde", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locale_fallback" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "251af8e57c9400e3eb58242fe5b8b1152b2a64fdf4cf632f923c38ccee6f2fa9" +dependencies = [ + "icu_locale_core", + "icu_locale_fallback_data", + "icu_provider", + "potential_utf", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locale_fallback_data" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "decf2a22ec8fa68f1a0c1129a3f8583f8f8bc24e8b9ccbe98ead99f62a4dc3a8" + +[[package]] +name = "icu_normalizer" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0" + +[[package]] +name = "icu_properties" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa" + +[[package]] +name = "icu_provider" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d27bbb9d3abbefac45d55f647c9de1d44aafcd1186eb91879afef17c396c3e73" +dependencies = [ + "displaydoc", + "icu_locale_core", + "serde", + "stable_deref_trait", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_segmenter" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82d07aafccd67af15d02512a6adf5896fbc5ed00f2e99b471d2efa14016db3db" +dependencies = [ + "icu_collections", + "icu_locale_fallback", + "icu_provider", + "icu_segmenter_data", + "potential_utf", + "smallvec", + "utf8_iter", + "zerovec", +] + +[[package]] +name = "icu_segmenter_data" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae293c039020f9ec10710af98d29ce6aa2051486638b49c9a6409f3b4a9e98ad" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "ipnet" +version = "2.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791930b43c0d5973160d90a8f3894509f2b273430f5c5c73b668636d0287c5c0" + +[[package]] +name = "is_ci" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45" + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +[[package]] +name = "jobserver" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" +dependencies = [ + "getrandom 0.4.3", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7883d941dae510fb2d978fc3fe018c71c9e2892fd38854de3e8b92c2e5ad9cc5" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + [[package]] name = "libc" version = "0.2.189" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + [[package]] name = "linux-raw-sys" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" +[[package]] +name = "litemap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" + [[package]] name = "lock_api" version = "0.4.14" @@ -75,12 +969,69 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "log" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" + +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + [[package]] name = "memchr" version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" +[[package]] +name = "miette" +version = "7.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f98efec8807c63c752b5bd61f862c165c115b0a35685bdcfd9238c7aeb592b7" +dependencies = [ + "backtrace", + "backtrace-ext", + "cfg-if", + "miette-derive", + "owo-colors", + "supports-color", + "supports-hyperlinks", + "supports-unicode", + "terminal_size", + "textwrap", + "unicode-width 0.1.14", +] + +[[package]] +name = "miette-derive" +version = "7.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db5b29714e950dbb20d5e6f74f9dcec4edbcc1067bb7f8ed198c097b8c1a818b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", +] + [[package]] name = "mio" version = "1.2.3" @@ -89,7 +1040,78 @@ checksum = "4b18443e9c262bfe8fa82f51666e2642c53393f7e5c27b3e1aeab922cff5b9d8" dependencies = [ "libc", "wasi", - "windows-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "multimap" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" + +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", +] + +[[package]] +name = "noyalib" +version = "0.0.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "962838abbd8dd2cd3844d2208dc502458bba6985f8bf1dc21761b30260fcbd7f" +dependencies = [ + "hashbrown 0.17.1", + "indexmap", + "libm", + "memchr", + "rustc-hash", + "serde_core", + "smallvec", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "oauth2" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51e219e79014df21a225b1860a479e2dcd7cbd9130f4defd4bd0e191ea31d67d" +dependencies = [ + "base64 0.22.1", + "chrono", + "getrandom 0.2.17", + "http", + "rand 0.8.8", + "reqwest", + "serde", + "serde_json", + "serde_path_to_error", + "sha2", + "thiserror 1.0.69", + "url", +] + +[[package]] +name = "object" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +dependencies = [ + "memchr", ] [[package]] @@ -102,13 +1124,103 @@ checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" name = "openshell-conformance" version = "0.0.0" dependencies = [ - "rand", + "rand 0.9.5", "serde", "serde_json", "tempfile", "tokio", ] +[[package]] +name = "openshell-conformance-sandbox-lifecycle" +version = "0.0.0" +dependencies = [ + "openshell-testkit", +] + +[[package]] +name = "openshell-core" +version = "0.0.0" +dependencies = [ + "async-trait", + "base64 0.22.1", + "chrono", + "glob", + "ipnet", + "miette", + "nix", + "openshell-extension-core", + "openshell-policy-schema", + "prost", + "prost-types", + "protoc-bin-vendored", + "reqwest", + "rustix", + "rustls", + "rustls-pemfile", + "serde", + "serde_json", + "sha2", + "thiserror 2.0.21", + "tokio", + "tokio-stream", + "tonic", + "tonic-prost", + "tonic-prost-build", + "tonic-types", + "tracing", + "url", + "uuid", +] + +[[package]] +name = "openshell-extension-core" +version = "0.0.0" +dependencies = [ + "hyper-util", + "serde", + "thiserror 2.0.21", + "tokio", + "tonic", + "tower", +] + +[[package]] +name = "openshell-policy-schema" +version = "0.0.0" +dependencies = [ + "miette", + "noyalib", + "serde", + "serde_json", + "serde_path_to_error", + "thiserror 2.0.21", +] + +[[package]] +name = "openshell-sdk" +version = "0.0.0" +dependencies = [ + "async-stream", + "async-trait", + "futures", + "hyper", + "hyper-util", + "miette", + "oauth2", + "openshell-core", + "reqwest", + "rustls", + "serde", + "thiserror 2.0.21", + "tokio", + "tokio-rustls", + "tokio-tungstenite", + "tonic", + "tower", + "tracing", +] + [[package]] name = "openshell-test-conformance-cli" version = "0.0.0" @@ -117,6 +1229,41 @@ dependencies = [ "tokio", ] +[[package]] +name = "openshell-testkit" +version = "0.0.0" +dependencies = [ + "futures", + "noyalib", + "openshell-sdk", + "openshell-testkit-macros", + "rand 0.9.5", + "serde", + "tokio", + "tonic", +] + +[[package]] +name = "openshell-testkit-macros" +version = "0.0.0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + +[[package]] +name = "owo-colors" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c45bb4a6ae1280ec0803b1ef9d3455eb50f01efbbe1447ab020f1d54fba9d8" + [[package]] name = "parking_lot" version = "0.12.5" @@ -140,12 +1287,66 @@ dependencies = [ "windows-link", ] +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "petgraph" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" +dependencies = [ + "fixedbitset", + "hashbrown 0.15.5", + "indexmap", +] + +[[package]] +name = "pin-project" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "pin-project-lite" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" +[[package]] +name = "pkg-config" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" + +[[package]] +name = "potential_utf" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d83eb9bc6d8e5cf568e7a1101d60ee05e81ed50ea106026f3d18deeb046d7661" +dependencies = [ + "serde_core", + "writeable", + "zerovec", +] + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -155,6 +1356,16 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.119", +] + [[package]] name = "proc-macro2" version = "1.0.107" @@ -164,6 +1375,143 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "prost" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "528ac67416ff8646872a3c02cad9cc4ee5dc9f9540c9b10771855c95cb2e5ae1" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03da047801ff44bb6a4d407d4860c05fd70bb81714e6b2f3812603d5b145b042" +dependencies = [ + "heck", + "itertools", + "log", + "multimap", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "pulldown-cmark", + "pulldown-cmark-to-cmark", + "regex", + "syn 2.0.119", + "tempfile", +] + +[[package]] +name = "prost-derive" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b570b25f7617e43d59005d0990ccb79e950a423952cea19671b7a876da390adf" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "prost-types" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f94967dc7688f3054c7fac87473ffae4cc4c3904800e2d9f5b857246d8963b0a" +dependencies = [ + "prost", +] + +[[package]] +name = "protoc-bin-vendored" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8760a25b6ff9c620324822737e468478fa092234190d2e449760344354896ed9" +dependencies = [ + "protoc-bin-vendored-linux-aarch_64", + "protoc-bin-vendored-linux-ppcle_64", + "protoc-bin-vendored-linux-s390_64", + "protoc-bin-vendored-linux-x86_32", + "protoc-bin-vendored-linux-x86_64", + "protoc-bin-vendored-macos-aarch_64", + "protoc-bin-vendored-macos-x86_64", + "protoc-bin-vendored-win32", +] + +[[package]] +name = "protoc-bin-vendored-linux-aarch_64" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73fa2624782ca04cd44f51554566717377acd240e4c0016d757dd74fccc9324f" + +[[package]] +name = "protoc-bin-vendored-linux-ppcle_64" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2417e9817fa237dab803ad4dda7357a111656e242959cc6b8f9a1a583367d42" + +[[package]] +name = "protoc-bin-vendored-linux-s390_64" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d189c34636356a46a7ed3188233dc8a88c431278cc54d4a19b096a2d270e985" + +[[package]] +name = "protoc-bin-vendored-linux-x86_32" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "171e39f1e846e5f322ced1ac3b8d4cd3a3833ca24b6e5d58b3632574fe6204fa" + +[[package]] +name = "protoc-bin-vendored-linux-x86_64" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "873cdcc097593432086661aa432b8078f1cd87bfb02847c332e98ae2c119e966" + +[[package]] +name = "protoc-bin-vendored-macos-aarch_64" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeb72df001783b8297847fe8f5f874ee400fd742c843d60583e8c23d96977c7f" + +[[package]] +name = "protoc-bin-vendored-macos-x86_64" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b04652167eca899dda05f32f5481adeaf25c623a98ce2fc146a001cc59a2add7" + +[[package]] +name = "protoc-bin-vendored-win32" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "263a3f48f01e7309e857138bd47f785585b4a005e8e56c6d2824ce91195999c3" + +[[package]] +name = "pulldown-cmark" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9f068eba8e7071c5f9511831b44f32c740d5adf574e990f946ddb53db2f314e" +dependencies = [ + "bitflags", + "memchr", + "unicase", +] + +[[package]] +name = "pulldown-cmark-to-cmark" +version = "22.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84bbb29c624230c4bd1047bbdb2aa47e41c860e9665ce62ba9504eebe91bf867" +dependencies = [ + "pulldown-cmark", +] + [[package]] name = "quote" version = "1.0.47" @@ -179,14 +1527,41 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e058c7de0b26af77780c769414d6257830bb240f3c38477dbc2c16e5f54d6d4c" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + [[package]] name = "rand" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" dependencies = [ - "rand_chacha", - "rand_core", + "rand_chacha 0.9.0", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", ] [[package]] @@ -196,7 +1571,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", ] [[package]] @@ -205,7 +1589,7 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" dependencies = [ - "getrandom", + "getrandom 0.3.4", ] [[package]] @@ -217,6 +1601,100 @@ dependencies = [ "bitflags", ] +[[package]] +name = "regex" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" + +[[package]] +name = "reqwest" +version = "0.12.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "rustls", + "rustls-native-certs", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b74b56ffa8bb2830709a538c2cbcae9aa062db0d2a42563bfb09bdaae44020eb" + +[[package]] +name = "rustc-hash" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" + [[package]] name = "rustix" version = "1.1.5" @@ -227,7 +1705,85 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls" +version = "0.23.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e13bd8c0e9365c43cfa5c9e8f9ad49d3c8444926c9aac819e0e4dc503c8fdf" +dependencies = [ + "aws-lc-rs", + "log", + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a073f5dc7469f984c52ad2752b63b0807745133b6de880b7b64c1ac4c48aec4" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "schannel" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys 0.61.2", ] [[package]] @@ -236,6 +1792,29 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "serde" version = "1.0.229" @@ -279,6 +1858,57 @@ dependencies = [ "zmij", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" +dependencies = [ + "itoa", + "serde", + "serde_core", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + [[package]] name = "signal-hook-registry" version = "1.4.8" @@ -289,6 +1919,12 @@ dependencies = [ "libc", ] +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + [[package]] name = "smallvec" version = "1.16.1" @@ -302,9 +1938,42 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.61.2", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "supports-color" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c64fc7232dd8d2e4ac5ce4ef302b1d81e0b80d055b9d77c7c4f51f6aa4c867d6" +dependencies = [ + "is_ci", +] + +[[package]] +name = "supports-hyperlinks" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e396b6523b11ccb83120b115a0b7366de372751aa6edf19844dfb13a6af97e91" + +[[package]] +name = "supports-unicode" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2" + [[package]] name = "syn" version = "2.0.119" @@ -327,6 +1996,26 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "901704edd0dfe137f1987838ee4f259e4e063c31371bdb423f7ae38ec6f77f02" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.5", +] + [[package]] name = "tempfile" version = "3.27.0" @@ -334,10 +2023,81 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom", + "getrandom 0.3.4", "once_cell", "rustix", - "windows-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "terminal_size" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874" +dependencies = [ + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "textwrap" +version = "0.16.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ecfad6c3abc80a577f2b91c1e412ee57e7a060d430b553c1b0c940974ebcd49" +dependencies = [ + "icu_segmenter", + "unicode-width 0.2.2", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e" +dependencies = [ + "thiserror-impl 2.0.21", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.5", +] + +[[package]] +name = "tinystr" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643" +dependencies = [ + "displaydoc", + "serde_core", + "zerovec", ] [[package]] @@ -354,7 +2114,7 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys", + "windows-sys 0.61.2", ] [[package]] @@ -368,12 +2128,330 @@ dependencies = [ "syn 3.0.5", ] +[[package]] +name = "tokio-rustls" +version = "0.26.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9cc2678c2cdd569ef8215e2afd7954ada2ae20b4fdd2c5fe6139a3b02d105db" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3d06f0b082ba57c26b79407372e57cf2a1e28124f78e9479fe80322cf53420b" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084" +dependencies = [ + "futures-util", + "log", + "rustls", + "rustls-native-certs", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tungstenite", +] + +[[package]] +name = "tokio-util" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e464cf451ba96ebfc6f9b6542f17ee8b8956e33f1e40d9690624e59d7a7f8a4b" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "libc", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tonic" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac2a5518c70fa84342385732db33fb3f44bc4cc748936eb5833d2df34d6445ef" +dependencies = [ + "async-trait", + "axum", + "base64 0.22.1", + "bytes", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-timeout", + "hyper-util", + "percent-encoding", + "pin-project", + "rustls-native-certs", + "socket2", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tokio-stream", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tonic-build" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c68f61875ac5293cf72e6c8cf0158086428c82c37229e98c840878f1706b0322" +dependencies = [ + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "tonic-prost" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50849f68853be452acf590cde0b146665b8d507b3b8af17261df47e02c209ea0" +dependencies = [ + "bytes", + "prost", + "tonic", +] + +[[package]] +name = "tonic-prost-build" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "654e5643eff75d7f8c99197ce1440ed19a3474eada74c12bbac488b2cafdae27" +dependencies = [ + "prettyplease", + "proc-macro2", + "prost-build", + "prost-types", + "quote", + "syn 2.0.119", + "tempfile", + "tonic-build", +] + +[[package]] +name = "tonic-types" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab1b02061f83d519bba3caa167f88f261ef05720ab8ebc954ade70de3348e8" +dependencies = [ + "prost", + "prost-types", + "tonic", +] + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "indexmap", + "pin-project-lite", + "slab", + "sync_wrapper", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", + "url", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "tungstenite" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" +dependencies = [ + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand 0.9.5", + "rustls", + "rustls-pki-types", + "sha1", + "thiserror 2.0.21", + "utf-8", +] + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "unicase" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357cc3acc6a036009fd6c973ed009037c732d60d0b4f6c673e9041497482a28f" + [[package]] name = "unicode-ident" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", + "serde_derive", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "uuid" +version = "1.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cc1186384beb7dd8eedea376413fd654937285ea6c9cfbb928dc3043ea4b606" +dependencies = [ + "getrandom 0.4.3", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "want" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec4cdd0dd910afe868b7ef477227d8d538b46b3075031afee8a9f2acb0a2ed0b" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" @@ -389,12 +2467,140 @@ dependencies = [ "wit-bindgen", ] +[[package]] +name = "wasm-bindgen" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb54f33acc68fd454578d9820b0bde1a1a3d17aa17bb7b6595806d02886d409" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cbab34de2d982e9b48e18d216d04c4a6f641066ff19ffb699980f591ee3610e" +dependencies = [ + "js-sys", + "tokio", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e29d0c35b16e224a7eeb5cd2d25e3e1968fbd65604117b44d3b789d00ee8535" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f501a8bc3719dba86ef8ae4728879c08001bea749eb1333ac5b91e040e2a6b7" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 3.0.5", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23f0c9c52aa7cd7d77769a4cfe2a9adb1b331f489a41d912ce14513d5ab995c6" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88261b9deccee56594c11a3460c462c41f58d148598fe70ad77070126a68aba4" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.61.2" @@ -404,12 +2610,105 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + [[package]] name = "wit-bindgen" version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" +[[package]] +name = "writeable" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" + +[[package]] +name = "yoke" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec8ebde2db3681e8c9980cc27822030e68752690ddfa9473e739aeb4dbde6d71" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.5", + "synstructure", +] + [[package]] name = "zerocopy" version = "0.8.57" @@ -430,6 +2729,68 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "zerofrom" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f75b4683f6c7f45248d4d64056a24298c6281e0993356d7d1b4a1a962ef10d4a" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.5", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13084392c5e4bc371903e2935a5eaeed24905a7511356b883835e18a78f6879" + +[[package]] +name = "zerotrie" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "zerovec" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8" +dependencies = [ + "serde", + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.5", +] + [[package]] name = "zmij" version = "1.0.23" diff --git a/tests/suites/conformance/Cargo.toml b/tests/suites/conformance/Cargo.toml index 1c6b00109..7aee0b273 100644 --- a/tests/suites/conformance/Cargo.toml +++ b/tests/suites/conformance/Cargo.toml @@ -3,4 +3,4 @@ [workspace] resolver = "2" -members = ["cli"] +members = ["cli", "sandbox-lifecycle", "testkit", "testkit-macros"] diff --git a/tests/suites/conformance/sandbox-lifecycle/Cargo.toml b/tests/suites/conformance/sandbox-lifecycle/Cargo.toml new file mode 100644 index 000000000..cf0ed9b09 --- /dev/null +++ b/tests/suites/conformance/sandbox-lifecycle/Cargo.toml @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +[package] +name = "openshell-conformance-sandbox-lifecycle" +description = "Conformance suite: sandbox-lifecycle" +version = "0.0.0" +edition = "2024" +rust-version = "1.94" +license = "Apache-2.0" +repository = "https://github.com/NVIDIA/OpenShell" +publish = false + +[dependencies] +openshell-testkit = { path = "../testkit" } diff --git a/tests/suites/conformance/sandbox-lifecycle/tests/sandbox_lifecycle.rs b/tests/suites/conformance/sandbox-lifecycle/tests/sandbox_lifecycle.rs new file mode 100644 index 000000000..b635943bb --- /dev/null +++ b/tests/suites/conformance/sandbox-lifecycle/tests/sandbox_lifecycle.rs @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +//! Conformance suite: sandbox-lifecycle. + +use testkit::Context; + +/// A long-running sandbox becomes ready, runs a command with exec, and is gone +/// from get and list after delete. +#[testkit::test] +async fn long_running_exec_delete(cx: &mut Context) -> testkit::Result { + let sandbox = cx + .sandbox("lr") + .command(["sleep", "infinity"]) + .create() + .await?; + sandbox.wait_ready().await?; + + let marker = format!("openshell-conformance-{}", cx.run_id()); + let output = sandbox.exec(["echo", marker.as_str()]).await?; + testkit::ensure!( + output.exit_code == 0, + "exec exited with {}; stderr: {}", + output.exit_code, + output.stderr + ); + testkit::ensure!( + output.stdout == format!("{marker}\n"), + "exec stdout was {:?}; expected {:?}", + output.stdout, + format!("{marker}\n") + ); + + sandbox.delete().await?; + cx.expect_absent(&sandbox).await +} diff --git a/tests/suites/conformance/testkit-macros/Cargo.toml b/tests/suites/conformance/testkit-macros/Cargo.toml new file mode 100644 index 000000000..6f632680e --- /dev/null +++ b/tests/suites/conformance/testkit-macros/Cargo.toml @@ -0,0 +1,21 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +[package] +name = "openshell-testkit-macros" +description = "The #[testkit::test] attribute for OpenShell conformance tests" +version = "0.0.0" +edition = "2024" +rust-version = "1.94" +license = "Apache-2.0" +repository = "https://github.com/NVIDIA/OpenShell" +publish = false + +[lib] +name = "testkit_macros" +proc-macro = true + +[dependencies] +proc-macro2 = "1" +quote = "1" +syn = { version = "2", features = ["full"] } diff --git a/tests/suites/conformance/testkit-macros/src/lib.rs b/tests/suites/conformance/testkit-macros/src/lib.rs new file mode 100644 index 000000000..6a52cb106 --- /dev/null +++ b/tests/suites/conformance/testkit-macros/src/lib.rs @@ -0,0 +1,240 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +//! The `#[testkit::test]` attribute. +//! +//! A conformance test is an `async fn` taking `&mut testkit::Context` and +//! returning `testkit::Result`. Its doc comment is the test's description and +//! is required. The function name is the test ID. Requirements are named +//! after target capabilities: +//! +//! ```ignore +//! /// An exposed loopback HTTP service is reachable and goes away after delete. +//! #[testkit::test(requires = [service_exposure])] +//! async fn expose_service(cx: &mut Context) -> testkit::Result { /* ... */ } +//! ``` +//! +//! The attribute expands to a `#[tokio::test]` wrapper with the same name, so +//! the nextest test name is the test ID. The original function is nested in +//! the wrapper and passed to `testkit::run`. + +use proc_macro::TokenStream; +use proc_macro2::{Span, TokenStream as TokenStream2}; +use quote::{format_ident, quote}; +use syn::parse::{Parse, ParseStream}; +use syn::punctuated::Punctuated; +use syn::{Expr, ExprLit, Ident, ItemFn, Lit, LitStr, Meta, Token, bracketed}; + +#[proc_macro_attribute] +pub fn test(attr: TokenStream, item: TokenStream) -> TokenStream { + expand(attr.into(), item.into()) + .unwrap_or_else(syn::Error::into_compile_error) + .into() +} + +/// Arguments accepted by `#[testkit::test(...)]`. +#[derive(Default)] +struct Args { + requires: Vec, +} + +impl Parse for Args { + fn parse(input: ParseStream<'_>) -> syn::Result { + let mut args = Self::default(); + while !input.is_empty() { + let key: Ident = input.parse()?; + if key != "requires" { + return Err(syn::Error::new( + key.span(), + format!("unknown argument `{key}`; expected `requires = [...]`"), + )); + } + input.parse::()?; + let content; + bracketed!(content in input); + args.requires + .extend(Punctuated::::parse_terminated(&content)?); + if !input.is_empty() { + input.parse::()?; + } + } + Ok(args) + } +} + +fn expand(attr: TokenStream2, item: TokenStream2) -> syn::Result { + let args: Args = syn::parse2(attr)?; + let mut body: ItemFn = syn::parse2(item)?; + + if body.sig.asyncness.is_none() { + return Err(syn::Error::new_spanned( + body.sig.fn_token, + "conformance tests must be `async fn`", + )); + } + if body.sig.inputs.len() != 1 { + return Err(syn::Error::new_spanned( + &body.sig.inputs, + "conformance tests take exactly one argument: `cx: &mut testkit::Context`", + )); + } + + let id = body.sig.ident.clone(); + let description = description(&body)?; + let requires = args.requires.iter().map(|capability| { + let variant = Ident::new(&to_camel_case(&capability.to_string()), capability.span()); + quote!(::testkit::Capability::#variant) + }); + + let vis = body.vis.clone(); + let wrapper_attrs: Vec<_> = body + .attrs + .iter() + .filter(|attr| attr.path().is_ident("doc")) + .cloned() + .collect(); + body.attrs.retain(|attr| !attr.path().is_ident("doc")); + body.vis = syn::Visibility::Inherited; + body.sig.ident = format_ident!("body"); + let id_str = LitStr::new(&id.to_string(), id.span()); + + Ok(quote! { + #(#wrapper_attrs)* + #[::testkit::__private::tokio::test(crate = "::testkit::__private::tokio")] + #vis async fn #id() { + #body + ::testkit::run( + ::testkit::TestInfo { + id: #id_str, + description: #description, + requires: &[#(#requires),*], + }, + body, + ) + .await; + } + }) +} + +/// The test description: the doc comment, joined into one line. +fn description(item: &ItemFn) -> syn::Result { + let lines: Vec = item + .attrs + .iter() + .filter_map(|attr| match &attr.meta { + Meta::NameValue(meta) if meta.path.is_ident("doc") => match &meta.value { + Expr::Lit(ExprLit { + lit: Lit::Str(line), + .. + }) => Some(line.value().trim().to_string()), + _ => None, + }, + _ => None, + }) + .filter(|line| !line.is_empty()) + .collect(); + + if lines.is_empty() { + return Err(syn::Error::new( + item.sig.ident.span(), + "conformance tests need a doc comment describing the behavior they verify", + )); + } + Ok(LitStr::new(&lines.join(" "), Span::call_site())) +} + +fn to_camel_case(snake: &str) -> String { + snake + .split('_') + .map(|word| { + let mut chars = word.chars(); + chars.next().map_or_else(String::new, |first| { + first.to_uppercase().chain(chars).collect() + }) + }) + .collect() +} + +#[cfg(test)] +mod tests { + use super::expand; + + fn expand_str(attr: &str, item: &str) -> syn::Result { + expand(attr.parse().unwrap(), item.parse().unwrap()).map(|tokens| tokens.to_string()) + } + + #[test] + fn expands_to_wrapper_with_same_name() { + let out = expand_str( + "", + "/// Does a thing.\nasync fn does_a_thing(cx: &mut Context) -> testkit::Result { Ok(()) }", + ) + .unwrap(); + assert!(out.contains("async fn does_a_thing ()"), "{out}"); + assert!(out.contains("async fn body (cx : & mut Context)"), "{out}"); + assert!(out.contains("id : \"does_a_thing\""), "{out}"); + assert!(out.contains("description : \"Does a thing.\""), "{out}"); + } + + #[test] + fn joins_multi_line_doc_comments() { + let out = expand_str( + "", + "/// First line\n/// second line.\nasync fn t(cx: &mut Context) -> testkit::Result { Ok(()) }", + ) + .unwrap(); + assert!(out.contains("\"First line second line.\""), "{out}"); + } + + #[test] + fn maps_requirements_to_capabilities() { + let out = expand_str( + "requires = [service_exposure, gpu]", + "/// Doc.\nasync fn t(cx: &mut Context) -> testkit::Result { Ok(()) }", + ) + .unwrap(); + assert!( + out.contains( + "requires : & [:: testkit :: Capability :: ServiceExposure , :: testkit :: Capability :: Gpu]" + ), + "{out}" + ); + } + + #[test] + fn rejects_missing_doc_comment() { + let err = expand_str( + "", + "async fn t(cx: &mut Context) -> testkit::Result { Ok(()) }", + ) + .unwrap_err(); + assert!(err.to_string().contains("doc comment"), "{err}"); + } + + #[test] + fn rejects_sync_fn() { + let err = expand_str( + "", + "/// Doc.\nfn t(cx: &mut Context) -> testkit::Result { Ok(()) }", + ) + .unwrap_err(); + assert!(err.to_string().contains("async fn"), "{err}"); + } + + #[test] + fn rejects_wrong_arity() { + let err = + expand_str("", "/// Doc.\nasync fn t() -> testkit::Result { Ok(()) }").unwrap_err(); + assert!(err.to_string().contains("exactly one argument"), "{err}"); + } + + #[test] + fn rejects_unknown_argument() { + let err = expand_str( + "timeout = [x]", + "/// Doc.\nasync fn t(cx: &mut Context) -> testkit::Result { Ok(()) }", + ) + .unwrap_err(); + assert!(err.to_string().contains("unknown argument"), "{err}"); + } +} diff --git a/tests/suites/conformance/testkit/Cargo.toml b/tests/suites/conformance/testkit/Cargo.toml new file mode 100644 index 000000000..b92cd65c1 --- /dev/null +++ b/tests/suites/conformance/testkit/Cargo.toml @@ -0,0 +1,25 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +[package] +name = "openshell-testkit" +description = "Shared support for OpenShell conformance tests" +version = "0.0.0" +edition = "2024" +rust-version = "1.94" +license = "Apache-2.0" +repository = "https://github.com/NVIDIA/OpenShell" +publish = false + +[lib] +name = "testkit" + +[dependencies] +openshell-sdk = { path = "../../../../crates/openshell-sdk" } +openshell-testkit-macros = { path = "../testkit-macros" } +futures = "0.3" +rand = "0.9" +serde = { version = "1", features = ["derive"] } +serde_yml = { package = "noyalib", version = "0.0.53", default-features = false, features = ["std", "compat-serde-yaml"] } +tokio = { version = "1.43", features = ["macros", "rt", "time"] } +tonic = { version = "0.14", features = ["tls-native-roots"] } diff --git a/tests/suites/conformance/testkit/src/context.rs b/tests/suites/conformance/testkit/src/context.rs new file mode 100644 index 000000000..dc9a74fa3 --- /dev/null +++ b/tests/suites/conformance/testkit/src/context.rs @@ -0,0 +1,285 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +//! Per-test context: the SDK client, the run ID, and the resources to clean up. + +use std::collections::BTreeSet; +use std::sync::{Arc, Mutex, PoisonError}; +use std::time::{Duration, Instant}; + +use openshell_sdk::{ + DeleteOptions, ExecOptions, ListOptions, SandboxPhase, SandboxRef, SandboxRestartPolicy, + SandboxSpec, SdkError, WorkspaceScopedClient, +}; +use rand::Rng; + +use crate::error::{Error, Result, ResultExt}; + +/// Sandbox creation includes image pulls on a cold environment. +pub const READY_TIMEOUT: Duration = Duration::from_mins(10); +pub const DELETE_TIMEOUT: Duration = Duration::from_mins(2); +pub const EXEC_TIMEOUT: Duration = Duration::from_mins(2); +const CLEANUP_TIMEOUT: Duration = Duration::from_mins(2); + +/// Names of sandboxes created by a test and not yet confirmed deleted. +type Registry = Arc>>; + +/// What a test uses to act on the system under test. +pub struct Context { + client: WorkspaceScopedClient, + run_id: String, + sandboxes: Registry, +} + +impl Context { + pub(crate) fn new(client: WorkspaceScopedClient) -> Self { + Self { + client, + run_id: generate_run_id(), + sandboxes: Registry::default(), + } + } + + /// The ID unique to this test run, used in resource names. + pub fn run_id(&self) -> &str { + &self.run_id + } + + /// The SDK client, for operations testkit does not wrap. + pub fn client(&self) -> &WorkspaceScopedClient { + &self.client + } + + /// A resource name unique to this run: `ct--`. + pub fn name(&self, suffix: &str) -> String { + format!("ct-{}-{suffix}", self.run_id) + } + + /// Start describing a sandbox named `cx.name(suffix)`. + pub fn sandbox(&self, suffix: &str) -> SandboxBuilder { + SandboxBuilder { + client: self.client.clone(), + sandboxes: Arc::clone(&self.sandboxes), + spec: SandboxSpec { + name: Some(self.name(suffix)), + ..SandboxSpec::default() + }, + } + } + + /// Fail unless the sandbox is gone from both get and list. + pub async fn expect_absent(&self, sandbox: &Sandbox) -> Result { + let name = sandbox.name(); + match self.client.get_sandbox(name).await { + Err(SdkError::NotFound { .. }) => {} + Err(error) => return Err(Error::from(error).context(format!("get sandbox {name}"))), + Ok(found) => { + return Err(Error::msg(format!( + "sandbox {name} still exists (id {}, phase {:?})", + found.id, found.phase + ))); + } + } + let listed = self + .client + .list_all_sandboxes(ListOptions::default()) + .await + .context("list sandboxes")?; + crate::ensure!( + !listed.iter().any(|entry| entry.name == name), + "sandbox {name} is absent from get but still listed" + ); + Ok(()) + } + + /// Delete every sandbox the test created and has not deleted. Returns the + /// cleanup failures; they are reported separately from the test result. + pub(crate) async fn cleanup(&self) -> Vec { + let started = Instant::now(); + let names: Vec = lock(&self.sandboxes).iter().cloned().collect(); + let mut failures = Vec::new(); + for name in names { + let remaining = CLEANUP_TIMEOUT.saturating_sub(started.elapsed()); + let result = async { + self.client + .delete_sandbox( + &name, + DeleteOptions { + allow_missing: true, + }, + ) + .await?; + self.client.wait_deleted(&name, remaining, None).await + } + .await; + match result { + Ok(()) => { + lock(&self.sandboxes).remove(&name); + } + Err(error) => failures.push(format!("delete sandbox {name}: {error}")), + } + } + failures + } +} + +/// A sandbox to create. +pub struct SandboxBuilder { + client: WorkspaceScopedClient, + sandboxes: Registry, + spec: SandboxSpec, +} + +impl SandboxBuilder { + /// The sandbox's main command. Empty selects the gateway's default. + #[must_use] + pub fn command(mut self, command: I) -> Self + where + I: IntoIterator, + S: Into, + { + self.spec.command = command.into_iter().map(Into::into).collect(); + self + } + + #[must_use] + pub fn restart_policy(mut self, policy: SandboxRestartPolicy) -> Self { + self.spec.restart_policy = policy; + self + } + + /// Full access to the spec for fields without a builder method. + #[must_use] + pub fn spec(mut self, update: impl FnOnce(&mut SandboxSpec)) -> Self { + update(&mut self.spec); + self + } + + /// Create the sandbox. It is tracked for cleanup before the request is + /// sent, so a create that fails after the gateway accepted it is still + /// cleaned up. + pub async fn create(self) -> Result { + let name = self.spec.name.clone().unwrap_or_default(); + lock(&self.sandboxes).insert(name.clone()); + let created = self + .client + .create_sandbox(self.spec) + .await + .context(format!("create sandbox {name}"))?; + Ok(Sandbox { + client: self.client, + sandboxes: self.sandboxes, + name, + id: created.id, + }) + } +} + +/// A sandbox created by the test. +pub struct Sandbox { + client: WorkspaceScopedClient, + sandboxes: Registry, + name: String, + id: String, +} + +/// The buffered output of an exec. +#[derive(Debug)] +pub struct ExecOutput { + pub exit_code: i32, + pub stdout: String, + pub stderr: String, +} + +impl Sandbox { + pub fn name(&self) -> &str { + &self.name + } + + pub fn id(&self) -> &str { + &self.id + } + + /// Wait until the sandbox is Ready. A sandbox that completes or fails + /// instead is an error. + pub async fn wait_ready(&self) -> Result { + let snapshot = self + .client + .wait_ready(&self.name, READY_TIMEOUT) + .await + .context(format!("wait for sandbox {} to be ready", self.name))?; + crate::ensure!( + snapshot.phase == SandboxPhase::Ready, + "sandbox {} reached phase {:?}; expected Ready", + self.name, + snapshot.phase + ); + Ok(snapshot) + } + + /// Run a command in the sandbox and buffer its output. + pub async fn exec(&self, command: I) -> Result + where + I: IntoIterator, + S: Into, + { + let command: Vec = command.into_iter().map(Into::into).collect(); + let options = ExecOptions { + timeout: Some(EXEC_TIMEOUT), + ..ExecOptions::default() + }; + let result = self + .client + .exec(&self.name, &command, options) + .await + .context(format!("exec {command:?} in sandbox {}", self.name))?; + Ok(ExecOutput { + exit_code: result.exit_code, + stdout: String::from_utf8_lossy(&result.stdout).into_owned(), + stderr: String::from_utf8_lossy(&result.stderr).into_owned(), + }) + } + + /// Delete the sandbox and wait until it is gone. + pub async fn delete(&self) -> Result { + self.client + .delete_sandbox(&self.name, DeleteOptions::default()) + .await + .context(format!("delete sandbox {}", self.name))?; + self.client + .wait_deleted(&self.name, DELETE_TIMEOUT, Some(&self.id)) + .await + .context(format!("wait for sandbox {} to be deleted", self.name))?; + lock(&self.sandboxes).remove(&self.name); + Ok(()) + } +} + +fn lock(registry: &Registry) -> std::sync::MutexGuard<'_, BTreeSet> { + registry.lock().unwrap_or_else(PoisonError::into_inner) +} + +/// Short, lowercase, and DNS-label safe, so names work on every driver. +fn generate_run_id() -> String { + const ALPHABET: &[u8] = b"0123456789abcdefghijklmnopqrstuvwxyz"; + let mut rng = rand::rng(); + (0..8) + .map(|_| char::from(ALPHABET[rng.random_range(0..ALPHABET.len())])) + .collect() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn run_ids_are_dns_label_safe() { + let id = generate_run_id(); + assert_eq!(id.len(), 8); + assert!( + id.bytes() + .all(|b| b.is_ascii_lowercase() || b.is_ascii_digit()), + "{id}" + ); + } +} diff --git a/tests/suites/conformance/testkit/src/error.rs b/tests/suites/conformance/testkit/src/error.rs new file mode 100644 index 000000000..75f00e5fd --- /dev/null +++ b/tests/suites/conformance/testkit/src/error.rs @@ -0,0 +1,112 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +//! Test failures. + +use std::fmt; + +use openshell_sdk::SdkError; + +/// A test failure: a message with the context it happened in. +#[derive(Debug)] +pub struct Error { + message: String, +} + +/// The result of a test or test step. +pub type Result = std::result::Result; + +impl Error { + pub fn msg(message: impl Into) -> Self { + Self { + message: message.into(), + } + } + + pub fn from_display(error: impl fmt::Display) -> Self { + Self::msg(error.to_string()) + } + + #[must_use] + pub fn context(self, context: impl fmt::Display) -> Self { + Self::msg(format!("{context}: {}", self.message)) + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&self.message) + } +} + +impl std::error::Error for Error {} + +impl From for Error { + fn from(error: SdkError) -> Self { + Self::from_display(error) + } +} + +/// Add context to a failing step. +pub trait ResultExt { + fn context(self, context: impl fmt::Display) -> Result; +} + +impl> ResultExt for std::result::Result { + fn context(self, context: impl fmt::Display) -> Result { + self.map_err(|error| error.into().context(context)) + } +} + +/// Fail the test unless `cond` holds. +/// +/// ```ignore +/// testkit::ensure!(output.exit_code == 0, "exec exited with {}", output.exit_code); +/// ``` +#[macro_export] +macro_rules! ensure { + ($cond:expr $(,)?) => { + if !$cond { + return Err($crate::Error::msg(concat!("condition failed: ", stringify!($cond)))); + } + }; + ($cond:expr, $($arg:tt)+) => { + if !$cond { + return Err($crate::Error::msg(format!($($arg)+))); + } + }; +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn context_prefixes_message() { + let result: Result = Err(Error::msg("boom")); + let err = result.context("step two").context("test").unwrap_err(); + assert_eq!(err.to_string(), "test: step two: boom"); + } + + #[test] + fn ensure_returns_error_with_message() { + fn check(value: i32) -> Result { + ensure!(value == 1, "value was {value}"); + Ok(()) + } + assert!(check(1).is_ok()); + assert_eq!(check(2).unwrap_err().to_string(), "value was 2"); + } + + #[test] + fn ensure_without_message_names_condition() { + fn check(value: i32) -> Result { + ensure!(value == 1); + Ok(()) + } + assert_eq!( + check(2).unwrap_err().to_string(), + "condition failed: value == 1" + ); + } +} diff --git a/tests/suites/conformance/testkit/src/lib.rs b/tests/suites/conformance/testkit/src/lib.rs new file mode 100644 index 000000000..1618683e3 --- /dev/null +++ b/tests/suites/conformance/testkit/src/lib.rs @@ -0,0 +1,45 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +//! Shared support for `OpenShell` conformance tests. +//! +//! Conformance tests are black-box tests of a running `OpenShell` system, +//! driven through the Rust SDK. Each test is an `async fn` marked +//! `#[testkit::test]`: +//! +//! ```ignore +//! use testkit::Context; +//! +//! /// A long-running sandbox becomes ready, runs exec, and is gone after delete. +//! #[testkit::test] +//! async fn long_running_exec_delete(cx: &mut Context) -> testkit::Result { +//! let sandbox = cx.sandbox("lr").command(["sleep", "infinity"]).create().await?; +//! sandbox.wait_ready().await?; +//! // ... +//! Ok(()) +//! } +//! ``` +//! +//! Tests read the system under test from the target file named by +//! `OPENSHELL_TEST_TARGET` (see [`target`]). + +mod context; +mod error; +mod poll; +mod run; +pub mod target; + +pub use context::{ + Context, DELETE_TIMEOUT, EXEC_TIMEOUT, ExecOutput, READY_TIMEOUT, Sandbox, SandboxBuilder, +}; +pub use error::{Error, Result, ResultExt}; +pub use openshell_sdk as sdk; +pub use poll::{Poll, poll_until}; +pub use run::{TestInfo, run}; +pub use target::{Capability, Target}; +pub use testkit_macros::test; + +#[doc(hidden)] +pub mod __private { + pub use tokio; +} diff --git a/tests/suites/conformance/testkit/src/poll.rs b/tests/suites/conformance/testkit/src/poll.rs new file mode 100644 index 000000000..1f46f49c3 --- /dev/null +++ b/tests/suites/conformance/testkit/src/poll.rs @@ -0,0 +1,90 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +//! Waiting for observable state. + +use std::time::{Duration, Instant}; + +use crate::error::{Error, Result}; + +/// The outcome of one observation. +pub enum Poll { + /// The condition holds. + Ready(T), + /// Not yet; the string describes what was observed, for the timeout report. + Pending(String), +} + +/// Observe until the condition holds or `timeout` elapses. +/// +/// An error from `observe` fails immediately. On timeout, the error reports +/// the last observation. +pub async fn poll_until( + what: &str, + timeout: Duration, + interval: Duration, + mut observe: impl AsyncFnMut() -> Result>, +) -> Result { + let started = Instant::now(); + loop { + match observe().await? { + Poll::Ready(value) => return Ok(value), + Poll::Pending(observation) if started.elapsed() >= timeout => { + return Err(Error::msg(format!( + "timed out after {timeout:?} waiting for {what}; last observation: {observation}" + ))); + } + Poll::Pending(_) => tokio::time::sleep(interval).await, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[tokio::test] + async fn returns_when_ready() { + let mut calls = 0; + let value = poll_until( + "three calls", + Duration::from_secs(5), + Duration::ZERO, + async || { + calls += 1; + Ok(if calls == 3 { + Poll::Ready(calls) + } else { + Poll::Pending(format!("call {calls}")) + }) + }, + ) + .await + .unwrap(); + assert_eq!(value, 3); + } + + #[tokio::test] + async fn reports_last_observation_on_timeout() { + let err = poll_until::<()>("never", Duration::ZERO, Duration::ZERO, async || { + Ok(Poll::Pending("still waiting".to_string())) + }) + .await + .unwrap_err(); + assert!(err.to_string().contains("waiting for never"), "{err}"); + assert!(err.to_string().contains("still waiting"), "{err}"); + } + + #[tokio::test] + async fn fails_fast_on_error() { + let err = poll_until::<()>( + "error", + Duration::from_mins(1), + Duration::ZERO, + async || Err(Error::msg("broken")), + ) + .await + .unwrap_err(); + assert_eq!(err.to_string(), "broken"); + } +} diff --git a/tests/suites/conformance/testkit/src/run.rs b/tests/suites/conformance/testkit/src/run.rs new file mode 100644 index 000000000..bc6b6515e --- /dev/null +++ b/tests/suites/conformance/testkit/src/run.rs @@ -0,0 +1,99 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +//! Running one test: load the target, check requirements, run, clean up. + +use std::panic::AssertUnwindSafe; + +use futures::FutureExt; + +use crate::context::Context; +use crate::error::Result; +use crate::target::{Capability, Target}; + +/// Identity of a test, generated by `#[testkit::test]`. +#[derive(Clone, Copy, Debug)] +pub struct TestInfo { + pub id: &'static str, + pub description: &'static str, + pub requires: &'static [Capability], +} + +/// Run a test body against the target named by `OPENSHELL_TEST_TARGET`. +/// +/// Cleanup runs whether the body passes, fails, or panics. The test fails if +/// the body fails, or if cleanup fails after the body passed. +/// +/// # Panics +/// +/// Panics to fail the test. +pub async fn run(info: TestInfo, body: F) +where + F: AsyncFnOnce(&mut Context) -> Result, +{ + let id = info.id; + eprintln!("[{id}] {}", info.description); + + let target = Target::from_env().unwrap_or_else(|error| panic!("[{id}] {error}")); + let missing = missing_requirements(&info, &target); + // The runner excludes tests the target can't support. Reaching here means + // it didn't, so fail rather than report a pass for an untested behavior. + assert!( + missing.is_empty(), + "[{id}] requires {}, which the target does not provide", + missing.join(", ") + ); + + let client = target + .connect() + .await + .unwrap_or_else(|error| panic!("[{id}] {error}")); + let mut cx = Context::new(client.workspace(&target.workspace)); + eprintln!("[{id}] run id {}", cx.run_id()); + + let outcome = AssertUnwindSafe(body(&mut cx)).catch_unwind().await; + let cleanup_failures = cx.cleanup().await; + for failure in &cleanup_failures { + eprintln!("[{id}] cleanup failed: {failure}"); + } + + match outcome { + Err(panic) => std::panic::resume_unwind(panic), + Ok(Err(error)) => panic!("[{id}] {error}"), + Ok(Ok(())) => assert!( + cleanup_failures.is_empty(), + "[{id}] passed, but cleanup failed: {}", + cleanup_failures.join("; ") + ), + } +} + +fn missing_requirements(info: &TestInfo, target: &Target) -> Vec<&'static str> { + info.requires + .iter() + .filter(|capability| !target.capabilities.provides(**capability)) + .map(|capability| capability.name()) + .collect() +} + +#[cfg(test)] +mod tests { + use std::path::PathBuf; + + use super::*; + + #[test] + fn reports_missing_requirements() { + let target = Target::parse( + "gateway:\n endpoint: http://x\ncapabilities:\n gpu: true\n", + PathBuf::new(), + ) + .unwrap(); + let info = TestInfo { + id: "t", + description: "d", + requires: &[Capability::Gpu, Capability::ServiceExposure], + }; + assert_eq!(missing_requirements(&info, &target), ["service_exposure"]); + } +} diff --git a/tests/suites/conformance/testkit/src/target.rs b/tests/suites/conformance/testkit/src/target.rs new file mode 100644 index 000000000..dc9509052 --- /dev/null +++ b/tests/suites/conformance/testkit/src/target.rs @@ -0,0 +1,291 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +//! The target: how tests reach the system under test, and what it provides. +//! +//! An environment writes a target file; tests find it through +//! [`TARGET_ENV`]. Relative paths in the file resolve against its directory. +//! +//! ```yaml +//! gateway: +//! endpoint: https://127.0.0.1:17670 +//! tls: +//! ca: ca.crt +//! client_cert: client.crt +//! client_key: client.key +//! workspace: default +//! capabilities: +//! compute_driver: docker +//! service_exposure: true +//! ``` + +use std::path::{Path, PathBuf}; +use std::time::Duration; + +use openshell_sdk::{AuthConfig, ClientConfig, EdgeAuthInterceptor, OpenShellClient}; +use serde::Deserialize; +use tonic::transport::{Certificate, ClientTlsConfig, Endpoint, Identity}; + +use crate::error::{Error, Result, ResultExt}; + +/// Environment variable naming the target file. +pub const TARGET_ENV: &str = "OPENSHELL_TEST_TARGET"; + +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct Target { + pub gateway: Gateway, + #[serde(default = "default_workspace")] + pub workspace: String, + #[serde(default)] + pub capabilities: Capabilities, + #[serde(skip)] + base_dir: PathBuf, +} + +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct Gateway { + /// Gateway URL; the scheme selects plaintext (`http`) or TLS (`https`). + pub endpoint: String, + #[serde(default)] + pub tls: Option, + #[serde(default)] + pub auth: Option, +} + +/// TLS material. A client certificate and key together select mTLS. +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct Tls { + pub ca: Option, + pub client_cert: Option, + pub client_key: Option, +} + +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct Auth { + pub bearer_token_file: PathBuf, +} + +/// Facts about the environment, declared by the environment definition. +#[derive(Debug, Default, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct Capabilities { + pub arch: Option, + pub compute_driver: Option, + #[serde(default)] + pub rootless: bool, + #[serde(default)] + pub gpu: bool, + #[serde(default)] + pub service_exposure: bool, +} + +/// A capability a test can require with `#[testkit::test(requires = [...])]`. +/// +/// Variants are the snake-case requirement names in CamelCase, so an unknown +/// requirement fails to compile. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Capability { + Gpu, + ServiceExposure, +} + +impl Capability { + pub const fn name(self) -> &'static str { + match self { + Self::Gpu => "gpu", + Self::ServiceExposure => "service_exposure", + } + } +} + +impl Capabilities { + pub const fn provides(&self, capability: Capability) -> bool { + match capability { + Capability::Gpu => self.gpu, + Capability::ServiceExposure => self.service_exposure, + } + } +} + +fn default_workspace() -> String { + "default".to_string() +} + +// Mirrors the SDK's endpoint settings for channels testkit builds itself. +const CONNECT_TIMEOUT: Duration = Duration::from_secs(10); +const KEEP_ALIVE_INTERVAL: Duration = Duration::from_secs(10); + +impl Target { + /// Load the target named by [`TARGET_ENV`]. + pub fn from_env() -> Result { + let path = std::env::var_os(TARGET_ENV).ok_or_else(|| { + Error::msg(format!( + "{TARGET_ENV} is not set; it must name the target file for the system under test" + )) + })?; + Self::from_path(Path::new(&path)) + } + + pub fn from_path(path: &Path) -> Result { + let yaml = std::fs::read_to_string(path) + .map_err(Error::from_display) + .context(format!("read target file {}", path.display()))?; + let base_dir = path + .parent() + .unwrap_or_else(|| Path::new(".")) + .to_path_buf(); + Self::parse(&yaml, base_dir).context(format!("load target file {}", path.display())) + } + + /// Parse a target, resolving relative paths against `base_dir`. + pub fn parse(yaml: &str, base_dir: PathBuf) -> Result { + let mut target: Self = serde_yml::from_str(yaml).map_err(Error::from_display)?; + target.base_dir = base_dir; + target.validate()?; + Ok(target) + } + + fn validate(&self) -> Result { + let gateway = &self.gateway; + let Some(tls) = &gateway.tls else { + return Ok(()); + }; + if tls.client_cert.is_some() != tls.client_key.is_some() { + return Err(Error::msg( + "gateway.tls.client_cert and gateway.tls.client_key must be set together", + )); + } + if !gateway.endpoint.starts_with("https://") { + return Err(Error::msg(format!( + "gateway.tls is set but gateway.endpoint {:?} is not https", + gateway.endpoint + ))); + } + Ok(()) + } + + fn resolve(&self, path: &Path) -> PathBuf { + self.base_dir.join(path) + } + + fn read(&self, path: &Path) -> Result> { + let path = self.resolve(path); + std::fs::read(&path) + .map_err(Error::from_display) + .context(format!("read {}", path.display())) + } + + /// Connect to the target's gateway. + pub async fn connect(&self) -> Result { + let gateway = &self.gateway; + let tls = gateway.tls.as_ref(); + let ca = tls + .and_then(|tls| tls.ca.as_deref()) + .map(|path| self.read(path)) + .transpose()?; + let token = gateway + .auth + .as_ref() + .map(|auth| { + self.read(&auth.bearer_token_file) + .map(|bytes| String::from_utf8_lossy(&bytes).trim().to_string()) + }) + .transpose()?; + + let identity = match tls.map(|tls| (&tls.client_cert, &tls.client_key)) { + Some((Some(cert), Some(key))) => { + Some(Identity::from_pem(self.read(cert)?, self.read(key)?)) + } + _ => None, + }; + + let Some(identity) = identity else { + let mut config = ClientConfig::new(gateway.endpoint.clone()); + config.ca_cert = ca; + config.auth = token.map(AuthConfig::oidc); + return OpenShellClient::connect(config) + .await + .context(format!("connect to {}", gateway.endpoint)); + }; + + // The SDK does not speak mTLS, so build the channel here and hand it + // to the SDK. Every SDK call, including exec, uses this channel. + let tls_config = ca.map_or_else( + || ClientTlsConfig::new().with_enabled_roots(), + |ca| ClientTlsConfig::new().ca_certificate(Certificate::from_pem(ca)), + ); + let channel = Endpoint::from_shared(gateway.endpoint.clone()) + .map_err(Error::from_display)? + .connect_timeout(CONNECT_TIMEOUT) + .http2_adaptive_window(true) + .http2_keep_alive_interval(KEEP_ALIVE_INTERVAL) + .keep_alive_while_idle(true) + .tls_config(tls_config.identity(identity)) + .map_err(Error::from_display)? + .connect() + .await + .map_err(Error::from_display) + .context(format!("connect to {} with mTLS", gateway.endpoint))?; + let interceptor = EdgeAuthInterceptor::new(token.as_deref(), None)?; + Ok(OpenShellClient::from_parts(channel, interceptor)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn parse(yaml: &str) -> Result { + Target::parse(yaml, PathBuf::from("/targets/vm")) + } + + #[test] + fn parses_mtls_target_and_resolves_relative_paths() { + let target = parse( + "gateway:\n endpoint: https://127.0.0.1:17670\n tls:\n ca: ca.crt\n client_cert: client.crt\n client_key: /abs/client.key\ncapabilities:\n compute_driver: docker\n service_exposure: true\n", + ) + .unwrap(); + let tls = target.gateway.tls.as_ref().unwrap(); + assert_eq!(target.workspace, "default"); + assert_eq!( + target.resolve(tls.ca.as_deref().unwrap()), + PathBuf::from("/targets/vm/ca.crt") + ); + assert_eq!( + target.resolve(tls.client_key.as_deref().unwrap()), + PathBuf::from("/abs/client.key") + ); + assert!(target.capabilities.provides(Capability::ServiceExposure)); + assert!(!target.capabilities.provides(Capability::Gpu)); + } + + #[test] + fn parses_plaintext_target() { + let target = parse("gateway:\n endpoint: http://127.0.0.1:17670\n").unwrap(); + assert!(target.gateway.tls.is_none()); + assert!(!target.capabilities.provides(Capability::ServiceExposure)); + } + + #[test] + fn rejects_unknown_fields() { + let err = parse("gateway:\n endpoint: http://x\n endpiont: http://y\n").unwrap_err(); + assert!(err.to_string().contains("endpiont"), "{err}"); + } + + #[test] + fn rejects_half_configured_client_identity() { + let err = parse("gateway:\n endpoint: https://x\n tls:\n client_cert: client.crt\n") + .unwrap_err(); + assert!(err.to_string().contains("set together"), "{err}"); + } + + #[test] + fn rejects_tls_on_plaintext_endpoint() { + let err = parse("gateway:\n endpoint: http://x\n tls:\n ca: ca.crt\n").unwrap_err(); + assert!(err.to_string().contains("not https"), "{err}"); + } +}