diff --git a/tvix/Cargo.lock b/tvix/Cargo.lock index b38ff3b48..74cfa1d45 100644 --- a/tvix/Cargo.lock +++ b/tvix/Cargo.lock @@ -126,6 +126,16 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "assert-json-diff" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "async-channel" version = "2.3.1" @@ -277,6 +287,12 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "auto-future" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c1e7e457ea78e524f48639f551fd79703ac3f2237f5ecccdf4708f8a75ad373" + [[package]] name = "auto_impl" version = "1.2.0" @@ -305,7 +321,7 @@ dependencies = [ "axum-macros", "bytes", "futures-util", - "http", + "http 1.1.0", "http-body", "http-body-util", "hyper", @@ -338,7 +354,7 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http", + "http 1.1.0", "http-body", "http-body-util", "mime", @@ -361,7 +377,7 @@ dependencies = [ "bytes", "futures-util", "headers", - "http", + "http 1.1.0", "http-body", "http-body-util", "mime", @@ -399,6 +415,36 @@ dependencies = [ "tokio", ] +[[package]] +name = "axum-test" +version = "16.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "017cbca2776229a7100ebee44e065fcf5baccea6fc4cb9e5bea8328d83863a03" +dependencies = [ + "anyhow", + "assert-json-diff", + "auto-future", + "axum", + "bytes", + "bytesize", + "cookie", + "http 1.1.0", + "http-body-util", + "hyper", + "hyper-util", + "mime", + "pretty_assertions", + "reserve-port", + "rust-multipart-rfc7578_2", + "serde", + "serde_json", + "serde_urlencoded", + "smallvec", + "tokio", + "tower 0.5.1", + "url", +] + [[package]] name = "backtrace" version = "0.3.74" @@ -438,7 +484,7 @@ version = "0.2.10" source = "git+https://github.com/liufuyang/bigtable_rs?rev=1818355a5373a5bc2c84287e3a4e3807154ac8ef#1818355a5373a5bc2c84287e3a4e3807154ac8ef" dependencies = [ "gcp_auth", - "http", + "http 1.1.0", "hyper-util", "log", "prost", @@ -533,6 +579,12 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" +[[package]] +name = "bytesize" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e368af43e418a04d52505cf3dbc23dda4e3407ae2fa99fd0e4f308ce546acc" + [[package]] name = "bzip2" version = "0.4.4" @@ -736,6 +788,16 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "time", + "version_check", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -1413,7 +1475,7 @@ dependencies = [ "bytes", "chrono", "home", - "http", + "http 1.1.0", "http-body-util", "hyper", "hyper-rustls", @@ -1515,7 +1577,7 @@ dependencies = [ "fnv", "futures-core", "futures-sink", - "http", + "http 1.1.0", "indexmap 2.6.0", "slab", "tokio", @@ -1565,7 +1627,7 @@ dependencies = [ "base64 0.21.7", "bytes", "headers-core", - "http", + "http 1.1.0", "httpdate", "mime", "sha1", @@ -1577,7 +1639,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" dependencies = [ - "http", + "http 1.1.0", ] [[package]] @@ -1625,6 +1687,17 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http" version = "1.1.0" @@ -1643,7 +1716,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http", + "http 1.1.0", ] [[package]] @@ -1654,7 +1727,7 @@ checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", "futures-util", - "http", + "http 1.1.0", "http-body", "pin-project-lite", ] @@ -1687,7 +1760,7 @@ dependencies = [ "futures-channel", "futures-util", "h2", - "http", + "http 1.1.0", "http-body", "httparse", "httpdate", @@ -1705,7 +1778,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" dependencies = [ "futures-util", - "http", + "http 1.1.0", "hyper", "hyper-util", "rustls", @@ -1738,7 +1811,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http", + "http 1.1.0", "http-body", "hyper", "pin-project-lite", @@ -2159,6 +2232,16 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -2237,6 +2320,7 @@ dependencies = [ "axum", "axum-extra", "axum-range", + "axum-test", "bytes", "clap", "data-encoding", @@ -2252,6 +2336,7 @@ dependencies = [ "prost-build", "rstest", "serde", + "sha2", "thiserror", "tokio", "tokio-listener", @@ -2263,6 +2348,7 @@ dependencies = [ "tower-otel-http-metrics", "tracing", "tracing-subscriber", + "tracing-test", "tvix-castore", "tvix-store", "tvix-tracing", @@ -2591,7 +2677,7 @@ checksum = "10a8a7f5f6ba7c1b286c2fbca0454eaba116f63bbe69ed250b642d36fbb04d80" dependencies = [ "async-trait", "bytes", - "http", + "http 1.1.0", "opentelemetry", ] @@ -2603,7 +2689,7 @@ checksum = "91cf61a1868dacc576bf2b2a1c3e9ab150af7272909e80085c3173384fe11f76" dependencies = [ "async-trait", "futures-core", - "http", + "http 1.1.0", "opentelemetry", "opentelemetry-proto", "opentelemetry_sdk", @@ -3303,7 +3389,7 @@ dependencies = [ "futures-core", "futures-util", "h2", - "http", + "http 1.1.0", "http-body", "http-body-util", "hyper", @@ -3344,7 +3430,7 @@ source = "git+https://github.com/TrueLayer/reqwest-middleware?rev=8a494c165734e2 dependencies = [ "anyhow", "async-trait", - "http", + "http 1.1.0", "reqwest", "serde", "thiserror", @@ -3359,7 +3445,7 @@ dependencies = [ "anyhow", "async-trait", "getrandom", - "http", + "http 1.1.0", "matchit 0.8.4", "opentelemetry", "reqwest", @@ -3368,6 +3454,16 @@ dependencies = [ "tracing-opentelemetry", ] +[[package]] +name = "reserve-port" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9838134a2bfaa8e1f40738fcc972ac799de6e0e06b5157acb95fc2b05a0ea283" +dependencies = [ + "lazy_static", + "thiserror", +] + [[package]] name = "ring" version = "0.17.8" @@ -3445,6 +3541,22 @@ dependencies = [ "syn 2.0.79", ] +[[package]] +name = "rust-multipart-rfc7578_2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b748410c0afdef2ebbe3685a6a862e2ee937127cdaae623336a459451c8d57" +dependencies = [ + "bytes", + "futures-core", + "futures-util", + "http 0.2.12", + "mime", + "mime_guess", + "rand", + "thiserror", +] + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -4334,7 +4446,7 @@ dependencies = [ "base64 0.22.1", "bytes", "h2", - "http", + "http 1.1.0", "http-body", "http-body-util", "hyper", @@ -4441,7 +4553,7 @@ dependencies = [ "bitflags 2.6.0", "bytes", "futures-core", - "http", + "http 1.1.0", "http-body", "http-body-util", "pin-project-lite", @@ -4466,7 +4578,7 @@ checksum = "ed0ba983713ec0f5d512dc28091fa3c1cb8fa5487de32a1b0bc0cb4159f9f89f" dependencies = [ "axum", "futures-util", - "http", + "http 1.1.0", "opentelemetry", "pin-project-lite", "tower 0.5.1", @@ -4582,6 +4694,27 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "tracing-test" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "557b891436fe0d5e0e363427fc7f217abf9ccd510d5136549847bdcbcd011d68" +dependencies = [ + "tracing-core", + "tracing-subscriber", + "tracing-test-macro", +] + +[[package]] +name = "tracing-test-macro" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04659ddb06c87d233c566112c1c9c5b9e98256d9af50ec3bc9c8327f873a7568" +dependencies = [ + "quote", + "syn 2.0.79", +] + [[package]] name = "tracing-tracy" version = "0.11.3" @@ -4924,7 +5057,7 @@ name = "tvix-tracing" version = "0.1.0" dependencies = [ "axum", - "http", + "http 1.1.0", "indicatif", "opentelemetry", "opentelemetry-http", diff --git a/tvix/Cargo.nix b/tvix/Cargo.nix index 32e2830d7..85399c0db 100644 --- a/tvix/Cargo.nix +++ b/tvix/Cargo.nix @@ -492,6 +492,34 @@ rec { "zeroize" = [ "dep:zeroize" ]; }; }; + "assert-json-diff" = rec { + crateName = "assert-json-diff"; + version = "2.0.2"; + edition = "2018"; + sha256 = "04mg3w0rh3schpla51l18362hsirl23q93aisws2irrj32wg5r27"; + libName = "assert_json_diff"; + authors = [ + "David Pedersen " + ]; + dependencies = [ + { + name = "serde"; + packageId = "serde"; + } + { + name = "serde_json"; + packageId = "serde_json"; + } + ]; + devDependencies = [ + { + name = "serde"; + packageId = "serde"; + features = [ "derive" ]; + } + ]; + + }; "async-channel" = rec { crateName = "async-channel"; version = "2.3.1"; @@ -1002,6 +1030,17 @@ rec { "portable-atomic" = [ "dep:portable-atomic" ]; }; }; + "auto-future" = rec { + crateName = "auto-future"; + version = "1.0.0"; + edition = "2021"; + sha256 = "0wykbakzh227vz6frx9p48zsq0wpswgmb7v3917m53m7gr2pw7iw"; + libName = "auto_future"; + authors = [ + "Joseph Lenton " + ]; + + }; "auto_impl" = rec { crateName = "auto_impl"; version = "1.2.0"; @@ -1070,7 +1109,7 @@ rec { } { name = "http"; - packageId = "http"; + packageId = "http 1.1.0"; } { name = "http-body"; @@ -1243,7 +1282,7 @@ rec { } { name = "http"; - packageId = "http"; + packageId = "http 1.1.0"; } { name = "http-body"; @@ -1331,7 +1370,7 @@ rec { } { name = "http"; - packageId = "http"; + packageId = "http 1.1.0"; } { name = "http-body"; @@ -1500,6 +1539,135 @@ rec { ]; features = { }; }; + "axum-test" = rec { + crateName = "axum-test"; + version = "16.4.0"; + edition = "2021"; + sha256 = "00rshs1qscm8pvjvjk7wlv7aqnygbw34xr5y1q8afab2fyibqz01"; + libName = "axum_test"; + authors = [ + "Joseph Lenton " + ]; + dependencies = [ + { + name = "anyhow"; + packageId = "anyhow"; + } + { + name = "assert-json-diff"; + packageId = "assert-json-diff"; + } + { + name = "auto-future"; + packageId = "auto-future"; + } + { + name = "axum"; + packageId = "axum"; + } + { + name = "bytes"; + packageId = "bytes"; + } + { + name = "bytesize"; + packageId = "bytesize"; + } + { + name = "cookie"; + packageId = "cookie"; + } + { + name = "http"; + packageId = "http 1.1.0"; + } + { + name = "http-body-util"; + packageId = "http-body-util"; + } + { + name = "hyper"; + packageId = "hyper"; + features = [ "http1" ]; + } + { + name = "hyper-util"; + packageId = "hyper-util"; + features = [ "client" "http1" "client-legacy" ]; + } + { + name = "mime"; + packageId = "mime"; + } + { + name = "pretty_assertions"; + packageId = "pretty_assertions"; + optional = true; + } + { + name = "reserve-port"; + packageId = "reserve-port"; + } + { + name = "rust-multipart-rfc7578_2"; + packageId = "rust-multipart-rfc7578_2"; + } + { + name = "serde"; + packageId = "serde"; + } + { + name = "serde_json"; + packageId = "serde_json"; + } + { + name = "serde_urlencoded"; + packageId = "serde_urlencoded"; + } + { + name = "smallvec"; + packageId = "smallvec"; + } + { + name = "tokio"; + packageId = "tokio"; + features = [ "rt" ]; + } + { + name = "tower"; + packageId = "tower 0.5.1"; + features = [ "util" "make" ]; + } + { + name = "url"; + packageId = "url"; + } + ]; + devDependencies = [ + { + name = "axum"; + packageId = "axum"; + features = [ "multipart" "tokio" "ws" ]; + } + { + name = "tokio"; + packageId = "tokio"; + features = [ "rt" "rt-multi-thread" "sync" "time" "macros" ]; + } + ]; + features = { + "all" = [ "pretty-assertions" "yaml" "msgpack" "reqwest" "shuttle" "typed-routing" "ws" ]; + "default" = [ "pretty-assertions" ]; + "msgpack" = [ "dep:rmp-serde" ]; + "pretty-assertions" = [ "dep:pretty_assertions" ]; + "reqwest" = [ "dep:reqwest" ]; + "shuttle" = [ "dep:shuttle-axum" ]; + "typed-routing" = [ "dep:axum-extra" ]; + "ws" = [ "axum/ws" "tokio/time" "dep:uuid" "dep:base64" "dep:tokio-tungstenite" "dep:futures-util" ]; + "yaml" = [ "dep:serde_yaml" ]; + }; + resolvedDefaultFeatures = [ "default" "pretty-assertions" ]; + }; "backtrace" = rec { crateName = "backtrace"; version = "0.3.74"; @@ -1618,7 +1786,7 @@ rec { } { name = "http"; - packageId = "http"; + packageId = "http 1.1.0"; } { name = "hyper-util"; @@ -1917,6 +2085,19 @@ rec { }; resolvedDefaultFeatures = [ "default" "std" ]; }; + "bytesize" = rec { + crateName = "bytesize"; + version = "1.3.0"; + edition = "2015"; + sha256 = "1k3aak70iwz4s2gsjbxf0ws4xnixqbdz6p2ha96s06748fpniqx3"; + authors = [ + "Hyunsik Choi " + ]; + features = { + "serde" = [ "dep:serde" ]; + }; + resolvedDefaultFeatures = [ "default" ]; + }; "bzip2" = rec { crateName = "bzip2"; version = "0.4.4"; @@ -2471,6 +2652,45 @@ rec { ]; features = { }; }; + "cookie" = rec { + crateName = "cookie"; + version = "0.18.1"; + edition = "2018"; + sha256 = "0iy749flficrlvgr3hjmf3igr738lk81n5akzf4ym4cs6cxg7pjd"; + authors = [ + "Sergio Benitez " + "Alex Crichton " + ]; + dependencies = [ + { + name = "time"; + packageId = "time"; + usesDefaultFeatures = false; + features = [ "std" "parsing" "formatting" "macros" ]; + } + ]; + buildDependencies = [ + { + name = "version_check"; + packageId = "version_check"; + } + ]; + features = { + "aes-gcm" = [ "dep:aes-gcm" ]; + "base64" = [ "dep:base64" ]; + "hkdf" = [ "dep:hkdf" ]; + "hmac" = [ "dep:hmac" ]; + "key-expansion" = [ "sha2" "hkdf" ]; + "percent-encode" = [ "percent-encoding" ]; + "percent-encoding" = [ "dep:percent-encoding" ]; + "private" = [ "aes-gcm" "base64" "rand" "subtle" ]; + "rand" = [ "dep:rand" ]; + "secure" = [ "private" "signed" "key-expansion" ]; + "sha2" = [ "dep:sha2" ]; + "signed" = [ "hmac" "sha2" "base64" "rand" "subtle" ]; + "subtle" = [ "dep:subtle" ]; + }; + }; "core-foundation" = rec { crateName = "core-foundation"; version = "0.9.4"; @@ -4481,7 +4701,7 @@ rec { } { name = "http"; - packageId = "http"; + packageId = "http 1.1.0"; } { name = "http-body-util"; @@ -4796,7 +5016,7 @@ rec { } { name = "http"; - packageId = "http"; + packageId = "http 1.1.0"; } { name = "indexmap"; @@ -4983,7 +5203,7 @@ rec { } { name = "http"; - packageId = "http"; + packageId = "http 1.1.0"; } { name = "httpdate"; @@ -5012,7 +5232,7 @@ rec { dependencies = [ { name = "http"; - packageId = "http"; + packageId = "http 1.1.0"; } ]; @@ -5116,7 +5336,33 @@ rec { ]; }; - "http" = rec { + "http 0.2.12" = rec { + crateName = "http"; + version = "0.2.12"; + edition = "2018"; + sha256 = "1w81s4bcbmcj9bjp7mllm8jlz6b31wzvirz8bgpzbqkpwmbvn730"; + authors = [ + "Alex Crichton " + "Carl Lerche " + "Sean McArthur " + ]; + dependencies = [ + { + name = "bytes"; + packageId = "bytes"; + } + { + name = "fnv"; + packageId = "fnv"; + } + { + name = "itoa"; + packageId = "itoa"; + } + ]; + + }; + "http 1.1.0" = rec { crateName = "http"; version = "1.1.0"; edition = "2018"; @@ -5163,7 +5409,7 @@ rec { } { name = "http"; - packageId = "http"; + packageId = "http 1.1.0"; } ]; @@ -5191,7 +5437,7 @@ rec { } { name = "http"; - packageId = "http"; + packageId = "http 1.1.0"; } { name = "http-body"; @@ -5268,7 +5514,7 @@ rec { } { name = "http"; - packageId = "http"; + packageId = "http 1.1.0"; } { name = "http-body"; @@ -5354,7 +5600,7 @@ rec { } { name = "http"; - packageId = "http"; + packageId = "http 1.1.0"; } { name = "hyper"; @@ -5505,7 +5751,7 @@ rec { } { name = "http"; - packageId = "http"; + packageId = "http 1.1.0"; } { name = "http-body"; @@ -6731,6 +6977,35 @@ rec { ]; }; + "mime_guess" = rec { + crateName = "mime_guess"; + version = "2.0.5"; + edition = "2015"; + sha256 = "03jmg3yx6j39mg0kayf7w4a886dl3j15y8zs119zw01ccy74zi7p"; + authors = [ + "Austin Bonander " + ]; + dependencies = [ + { + name = "mime"; + packageId = "mime"; + } + { + name = "unicase"; + packageId = "unicase"; + } + ]; + buildDependencies = [ + { + name = "unicase"; + packageId = "unicase"; + } + ]; + features = { + "default" = [ "rev-mappings" ]; + }; + resolvedDefaultFeatures = [ "default" "rev-mappings" ]; + }; "minimal-lexical" = rec { crateName = "minimal-lexical"; version = "0.2.1"; @@ -7072,6 +7347,10 @@ rec { name = "tracing-subscriber"; packageId = "tracing-subscriber"; } + { + name = "tracing-test"; + packageId = "tracing-test"; + } { name = "tvix-castore"; packageId = "tvix-castore"; @@ -7101,6 +7380,10 @@ rec { } ]; devDependencies = [ + { + name = "axum-test"; + packageId = "axum-test"; + } { name = "hex-literal"; packageId = "hex-literal"; @@ -7109,6 +7392,10 @@ rec { name = "rstest"; packageId = "rstest"; } + { + name = "sha2"; + packageId = "sha2"; + } ]; features = { "default" = [ "otlp" ]; @@ -8243,7 +8530,7 @@ rec { } { name = "http"; - packageId = "http"; + packageId = "http 1.1.0"; usesDefaultFeatures = false; features = [ "std" ]; } @@ -8277,7 +8564,7 @@ rec { } { name = "http"; - packageId = "http"; + packageId = "http 1.1.0"; optional = true; usesDefaultFeatures = false; features = [ "std" ]; @@ -10480,7 +10767,7 @@ rec { } { name = "http"; - packageId = "http"; + packageId = "http 1.1.0"; } { name = "http-body"; @@ -10771,7 +11058,7 @@ rec { } { name = "http"; - packageId = "http"; + packageId = "http 1.1.0"; } { name = "reqwest"; @@ -10837,7 +11124,7 @@ rec { } { name = "http"; - packageId = "http"; + packageId = "http 1.1.0"; } { name = "matchit"; @@ -10904,6 +11191,27 @@ rec { }; resolvedDefaultFeatures = [ "opentelemetry_0_27" "opentelemetry_0_27_pkg" "tracing-opentelemetry_0_28_pkg" ]; }; + "reserve-port" = rec { + crateName = "reserve-port"; + version = "2.0.1"; + edition = "2021"; + sha256 = "10x21rdb1hjzp6n5flbbw3hfd7brmirckz1q0zsf3a7s5d516f4q"; + libName = "reserve_port"; + authors = [ + "Joseph Lenton " + ]; + dependencies = [ + { + name = "lazy_static"; + packageId = "lazy_static"; + } + { + name = "thiserror"; + packageId = "thiserror"; + } + ]; + + }; "ring" = rec { crateName = "ring"; version = "0.17.8"; @@ -11141,6 +11449,63 @@ rec { } ]; + }; + "rust-multipart-rfc7578_2" = rec { + crateName = "rust-multipart-rfc7578_2"; + version = "0.6.1"; + edition = "2021"; + sha256 = "0mwd3i2mk91n6diaxnkw28vyjbifhrm5ls73pcpfzz8a1i0lidq3"; + libName = "rust_multipart_rfc7578_2"; + authors = [ + "Joseph Lenton " + "Ferris Tseng " + ]; + dependencies = [ + { + name = "bytes"; + packageId = "bytes"; + } + { + name = "futures-core"; + packageId = "futures-core"; + } + { + name = "futures-util"; + packageId = "futures-util"; + usesDefaultFeatures = false; + features = [ "io" ]; + } + { + name = "http"; + packageId = "http 0.2.12"; + } + { + name = "mime"; + packageId = "mime"; + } + { + name = "mime_guess"; + packageId = "mime_guess"; + } + { + name = "rand"; + packageId = "rand"; + features = [ "small_rng" ]; + } + { + name = "thiserror"; + packageId = "thiserror"; + } + ]; + devDependencies = [ + { + name = "futures-util"; + packageId = "futures-util"; + usesDefaultFeatures = false; + features = [ "std" ]; + } + ]; + }; "rustc-demangle" = rec { crateName = "rustc-demangle"; @@ -13068,7 +13433,7 @@ rec { "std" = [ "alloc" "deranged/std" ]; "wasm-bindgen" = [ "dep:js-sys" ]; }; - resolvedDefaultFeatures = [ "alloc" "formatting" "parsing" "serde" "serde-well-known" "std" ]; + resolvedDefaultFeatures = [ "alloc" "formatting" "macros" "parsing" "serde" "serde-well-known" "std" ]; }; "time-core" = rec { crateName = "time-core"; @@ -13956,7 +14321,7 @@ rec { } { name = "http"; - packageId = "http"; + packageId = "http 1.1.0"; } { name = "http-body"; @@ -14484,7 +14849,7 @@ rec { } { name = "http"; - packageId = "http"; + packageId = "http 1.1.0"; } { name = "http-body"; @@ -14608,7 +14973,7 @@ rec { } { name = "http"; - packageId = "http"; + packageId = "http 1.1.0"; usesDefaultFeatures = false; features = [ "std" ]; } @@ -15072,6 +15437,57 @@ rec { }; resolvedDefaultFeatures = [ "alloc" "ansi" "default" "env-filter" "fmt" "matchers" "nu-ansi-term" "once_cell" "regex" "registry" "sharded-slab" "smallvec" "std" "thread_local" "tracing" "tracing-log" ]; }; + "tracing-test" = rec { + crateName = "tracing-test"; + version = "0.2.5"; + edition = "2018"; + sha256 = "0s0x076wpga7k1a3cl8da76rrgvs45zzq9rl6q75w3gy6qa8jysm"; + libName = "tracing_test"; + authors = [ + "Danilo Bargen " + ]; + dependencies = [ + { + name = "tracing-core"; + packageId = "tracing-core"; + } + { + name = "tracing-subscriber"; + packageId = "tracing-subscriber"; + features = [ "env-filter" ]; + } + { + name = "tracing-test-macro"; + packageId = "tracing-test-macro"; + } + ]; + features = { + "no-env-filter" = [ "tracing-test-macro/no-env-filter" ]; + }; + }; + "tracing-test-macro" = rec { + crateName = "tracing-test-macro"; + version = "0.2.5"; + edition = "2018"; + sha256 = "0s3m7a3pycn8r4xyql5gv5b85sdrqp4w24k1aqy26zf80vdrsr84"; + procMacro = true; + libName = "tracing_test_macro"; + authors = [ + "Danilo Bargen " + ]; + dependencies = [ + { + name = "quote"; + packageId = "quote"; + } + { + name = "syn"; + packageId = "syn 2.0.79"; + features = [ "full" ]; + } + ]; + features = { }; + }; "tracing-tracy" = rec { crateName = "tracing-tracy"; version = "0.11.3"; @@ -16446,7 +16862,7 @@ rec { } { name = "http"; - packageId = "http"; + packageId = "http 1.1.0"; optional = true; } { diff --git a/tvix/Cargo.toml b/tvix/Cargo.toml index eaf5bd334..c4b2dd4dc 100644 --- a/tvix/Cargo.toml +++ b/tvix/Cargo.toml @@ -149,6 +149,7 @@ tracing = "0.1.40" tracing-indicatif = "0.3.6" tracing-opentelemetry = "0.28.0" tracing-subscriber = "0.3.18" +tracing-test = "0.2.5" tracing-tracy = "0.11.2" trybuild = "1.0.99" url = "2.5.2" diff --git a/tvix/nar-bridge/Cargo.toml b/tvix/nar-bridge/Cargo.toml index 33fe2bd87..ae3d8b6d5 100644 --- a/tvix/nar-bridge/Cargo.toml +++ b/tvix/nar-bridge/Cargo.toml @@ -28,6 +28,7 @@ tvix-store = { path = "../store" } tvix-tracing = { path = "../tracing", features = ["tonic", "axum"] } tracing = { workspace = true } tracing-subscriber = { workspace = true } +tracing-test = { workspace = true } url = { workspace = true } serde = { workspace = true, features = ["derive"] } lru = { workspace = true } @@ -44,8 +45,10 @@ otlp = ["tvix-tracing/otlp", "tower-otel-http-metrics"] xp-store-composition-cli = ["tvix-store/xp-composition-cli"] [dev-dependencies] +axum-test = "16.4.0" hex-literal = { workspace = true } rstest = { workspace = true } +sha2.workspace = true [lints] workspace = true diff --git a/tvix/nar-bridge/src/nar.rs b/tvix/nar-bridge/src/nar.rs index c351c9e4b..15aea3fa4 100644 --- a/tvix/nar-bridge/src/nar.rs +++ b/tvix/nar-bridge/src/nar.rs @@ -18,7 +18,7 @@ use crate::AppState; #[derive(Debug, Deserialize)] pub(crate) struct GetNARParams { #[serde(rename = "narsize")] - nar_size: u64, + nar_size: Option, } #[instrument(skip_all)] @@ -34,6 +34,13 @@ pub async fn get_head( }): axum::extract::State, ) -> Result { use prost::Message; + // We insist on the nar_size field being set. + // If it's not present, the client is misbehaving somehow. + let nar_size = nar_size.ok_or_else(|| { + warn!("no nar_size parameter set"); + StatusCode::BAD_REQUEST + })?; + // b64decode the root node passed *by the user* let root_node_proto = BASE64URL_NOPAD .decode(root_node_enc.as_bytes()) @@ -57,7 +64,7 @@ pub async fn get_head( let root_node = root_node.try_into_anonymous_node().map_err(|e| { warn!(err=%e, "root node validation failed"); - StatusCode::BAD_REQUEST + StatusCode::NOT_FOUND })?; Ok(( @@ -116,7 +123,7 @@ pub async fn get_head( } /// Handler to respond to GET/HEAD requests for recently uploaded NAR files. -/// Nix probes at {narhash}.nar[.compression_suffix] to determine whether a NAR +/// Nix probes at {filehash}.nar[.compression_suffix] to determine whether a NAR /// has already been uploaded, by responding to (some of) these requests we /// avoid it unnecessarily uploading. /// We don't keep a full K/V from NAR hash to root note around, only the @@ -205,3 +212,264 @@ pub async fn put( Ok("") } + +#[cfg(test)] +mod tests { + use std::{ + num::NonZero, + sync::{Arc, LazyLock}, + }; + + use axum::{http::Method, Router}; + use bytes::Bytes; + use data_encoding::BASE64URL_NOPAD; + use nix_compat::nixbase32; + use sha2::Digest; + use tracing_test::traced_test; + use tvix_castore::{ + blobservice::{BlobService, MemoryBlobService}, + directoryservice::{DirectoryService, MemoryDirectoryService}, + fixtures::HELLOWORLD_BLOB_DIGEST, + }; + use tvix_store::{ + fixtures::{ + CASTORE_NODE_COMPLICATED, CASTORE_NODE_SYMLINK, NAR_CONTENTS_COMPLICATED, + NAR_CONTENTS_HELLOWORLD, NAR_CONTENTS_SYMLINK, + }, + pathinfoservice::{MemoryPathInfoService, PathInfoService}, + }; + + use crate::AppState; + + pub static NAR_STR_SYMLINK: LazyLock = LazyLock::new(|| { + use prost::Message; + BASE64URL_NOPAD.encode( + &tvix_castore::proto::Node::from_name_and_node("".into(), CASTORE_NODE_SYMLINK.clone()) + .encode_to_vec(), + ) + }); + + /// Accepts a router without state, and returns a [axum_test::TestServer]. + fn gen_server( + router: axum::Router, + ) -> ( + axum_test::TestServer, + impl BlobService, + impl DirectoryService, + impl PathInfoService, + ) { + let blob_service = Arc::new(MemoryBlobService::default()); + let directory_service = Arc::new(MemoryDirectoryService::default()); + let path_info_service = Arc::new(MemoryPathInfoService::default()); + + let app = router.with_state(AppState::new( + blob_service.clone(), + directory_service.clone(), + path_info_service.clone(), + NonZero::new(100).unwrap(), + )); + + ( + axum_test::TestServer::new(app).unwrap(), + blob_service, + directory_service, + path_info_service, + ) + } + + #[traced_test] + #[tokio::test] + async fn test_get_head() { + let (server, _blob_service, _directory_service, _path_info_service) = + gen_server(Router::new().route( + "/nar/tvix-castore/:root_node_enc", + axum::routing::get(super::get_head), + )); + + // Empty nar_str should be NotFound + server + .method(Method::HEAD, "/nar/tvix-castore/") + .expect_failure() + .await + .assert_status_not_found(); + + let valid_url = &format!("/nar/tvix-castore/{}", &*NAR_STR_SYMLINK); + let qps = &[("narsize", &NAR_CONTENTS_SYMLINK.len().to_string())]; + + // Missing narsize should be BadRequest + server + .method(Method::HEAD, valid_url) + .expect_failure() + .await + .assert_status_bad_request(); + + let invalid_url = { + use prost::Message; + let n = tvix_castore::proto::Node { + node: Some(tvix_castore::proto::node::Node::Directory( + tvix_castore::proto::DirectoryNode { + name: "".into(), + digest: "invalid b64".into(), + size: 1, + }, + )), + }; + &format!( + "/nar/tvix-castore/{}", + BASE64URL_NOPAD.encode(&n.encode_to_vec()) + ) + }; + + // Invalid node proto should return NotFound + server + .method(Method::HEAD, invalid_url) + .add_query_params(qps) + .expect_failure() + .await + .assert_status_not_found(); + + // success, HEAD + server + .method(Method::HEAD, valid_url) + .add_query_params(qps) + .expect_success() + .await; + + // success, GET + assert_eq!( + NAR_CONTENTS_SYMLINK.as_slice(), + server + .get(valid_url) + .add_query_params(qps) + .expect_success() + .await + .into_bytes(), + "Expected to get back NAR_CONTENTS_SYMLINK" + ) + } + + /// Uploading a NAR with a different file hash than what's specified in the URL + /// is considered an error. + #[traced_test] + #[tokio::test] + async fn test_put_wrong_narhash() { + let (server, _blob_service, _directory_service, _path_info_service) = + gen_server(Router::new().route("/nar/:nar_str", axum::routing::put(super::put))); + + server + .put("/nar/0000000000000000000000000000000000000000000000000000.nar") + .bytes(Bytes::from_static(&NAR_CONTENTS_SYMLINK)) + .expect_failure() + .await; + } + + /// Uploading a NAR with compression is not supported. + #[traced_test] + #[tokio::test] + async fn test_put_with_compression_fail() { + let (server, _blob_service, _directory_service, _path_info_service) = + gen_server(Router::new().route("/nar/:nar_str", axum::routing::put(super::put))); + + let nar_sha256: [u8; 32] = sha2::Sha256::new_with_prefix(NAR_CONTENTS_SYMLINK.as_slice()) + .finalize() + .into(); + + let nar_url = format!("/nar/{}.nar.zst", nixbase32::encode(&nar_sha256)); + + server + .put(&nar_url) + .bytes(Bytes::from_static(&NAR_CONTENTS_SYMLINK)) + .expect_failure() + .await + .assert_status_unauthorized(); + } + + /// Upload a NAR with a single file, ensure the blob exists later on. + #[traced_test] + #[tokio::test] + async fn test_put_success() { + let (server, blob_service, _directory_service, _path_info_service) = + gen_server(Router::new().route("/nar/:nar_str", axum::routing::put(super::put))); + + let nar_sha256: [u8; 32] = + sha2::Sha256::new_with_prefix(NAR_CONTENTS_HELLOWORLD.as_slice()) + .finalize() + .into(); + + let nar_url = format!("/nar/{}.nar", nixbase32::encode(&nar_sha256)); + + server + .put(&nar_url) + .bytes(Bytes::from_static(&NAR_CONTENTS_HELLOWORLD)) + .expect_success() + .await; + + assert!(blob_service + .has(&HELLOWORLD_BLOB_DIGEST) + .await + .expect("blobservice")) + } + + // Upload a NAR with blobs and directories, ensure blobs and directories + // were uploaded, by rendering the NAR stream from the root node we know + // describes these contents. + #[traced_test] + #[tokio::test] + async fn test_put_success2() { + let (server, blob_service, directory_service, _path_info_service) = + gen_server(Router::new().route("/nar/:nar_str", axum::routing::put(super::put))); + + let nar_sha256: [u8; 32] = + sha2::Sha256::new_with_prefix(NAR_CONTENTS_COMPLICATED.as_slice()) + .finalize() + .into(); + + let nar_url = format!("/nar/{}.nar", nixbase32::encode(&nar_sha256)); + + server + .put(&nar_url) + .bytes(Bytes::from_static(&NAR_CONTENTS_COMPLICATED)) + .expect_success() + .await; + + let mut buf = Vec::new(); + tvix_store::nar::write_nar( + &mut buf, + &CASTORE_NODE_COMPLICATED, + blob_service, + directory_service, + ) + .await + .expect("write nar"); + + assert_eq!(NAR_CONTENTS_COMPLICATED, buf[..]); + } + + /// Upload a NAR, ensure a HEAD by NarHash returns a 2xx code. + #[traced_test] + #[tokio::test] + async fn test_put_root_nodes() { + let (server, _blob_service, _directory_servicee, _path_info_service) = gen_server( + Router::new() + .route("/nar/:nar_str", axum::routing::put(super::put)) + .route("/nar/:nar_str", axum::routing::get(super::head_root_nodes)), + ); + + let nar_sha256: [u8; 32] = + sha2::Sha256::new_with_prefix(NAR_CONTENTS_COMPLICATED.as_slice()) + .finalize() + .into(); + + let nar_url = format!("/nar/{}.nar", nixbase32::encode(&nar_sha256)); + + // upload NAR + server + .put(&nar_url) + .bytes(Bytes::from_static(&NAR_CONTENTS_COMPLICATED)) + .expect_success() + .await; + + // check HEAD by NarHash + server.method(Method::HEAD, &nar_url).expect_success().await; + } +}