diff --git a/Cargo.lock b/Cargo.lock index 3e75210d..d3421911 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -74,6 +74,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + [[package]] name = "async-trait" version = "0.1.73" @@ -85,12 +91,102 @@ dependencies = [ "syn", ] +[[package]] +name = "auto-future" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c1e7e457ea78e524f48639f551fd79703ac3f2237f5ecccdf4708f8a75ad373" + [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "axum" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +dependencies = [ + "async-trait", + "axum-core", + "axum-macros", + "bitflags 1.3.2", + "bytes", + "futures-util", + "http", + "http-body", + "hyper", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "mime", + "rustversion", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-macros" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdca6a10ecad987bda04e95606ef85a5417dcaac1a78455242d72e031e2b6b62" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "axum-test" +version = "12.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d2978d45956c95e1c4ae0aea146a926360ba8faf78b4ab3cbf30451eb0879d9" +dependencies = [ + "anyhow", + "auto-future", + "axum", + "bytes", + "cookie", + "http", + "hyper", + "reserve-port", + "serde", + "serde_json", + "serde_urlencoded", + "smallvec", + "tokio", + "url", +] + [[package]] name = "backtrace" version = "0.3.69" @@ -279,6 +375,16 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "cookie" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7efb37c3e1ccb1ff97164ad95ac1606e8ccd35b3fa0a7d99a304c7f4a428cc24" +dependencies = [ + "time", + "version_check", +] + [[package]] name = "cpufeatures" version = "0.2.9" @@ -344,6 +450,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "deranged" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" + [[package]] name = "digest" version = "0.10.7" @@ -438,6 +550,8 @@ name = "fh" version = "0.1.6" dependencies = [ "async-trait", + "axum", + "axum-test", "clap", "clap_complete", "color-eyre", @@ -829,6 +943,12 @@ dependencies = [ "regex-automata 0.1.10", ] +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + [[package]] name = "memchr" version = "2.6.3" @@ -1033,6 +1153,26 @@ dependencies = [ "sha2", ] +[[package]] +name = "pin-project" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "pin-project-lite" version = "0.2.13" @@ -1195,6 +1335,16 @@ dependencies = [ "winreg", ] +[[package]] +name = "reserve-port" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b212efd3460286cd590149feedd0afabef08ee352445dd6b4452f0d136098a5f" +dependencies = [ + "lazy_static", + "thiserror", +] + [[package]] name = "ring" version = "0.16.20" @@ -1334,6 +1484,16 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4beec8bce849d58d06238cb50db2e1c417cfeafa4c63f692b15c82b7c80f8335" +dependencies = [ + "itoa", + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -1454,6 +1614,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "term" version = "0.7.0" @@ -1495,6 +1661,34 @@ dependencies = [ "once_cell", ] +[[package]] +name = "time" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "426f806f4089c493dcac0d24c29c01e2c38baf8e30f1b716ee37e83d200b18fe" +dependencies = [ + "deranged", + "itoa", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" +dependencies = [ + "time-core", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -1564,6 +1758,28 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + [[package]] name = "tower-service" version = "0.3.2" @@ -1577,6 +1793,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", + "log", "pin-project-lite", "tracing-attributes", "tracing-core", diff --git a/Cargo.toml b/Cargo.toml index 277baf97..e7831b7e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,3 +48,7 @@ tracing-subscriber = { version = "0.3.17", default-features = false, features = ] } url = { version = "2.4.0", default-features = false, features = ["serde"] } urlencoding = "2.1.3" + +[dev-dependencies] +axum = { version = "0.6.20", features = ["macros"] } +axum-test = "12.5.0" diff --git a/src/cli/cmd/convert.rs b/src/cli/cmd/convert.rs index d8bcb3f1..c56e795a 100644 --- a/src/cli/cmd/convert.rs +++ b/src/cli/cmd/convert.rs @@ -614,3 +614,113 @@ async fn convert_github_input_to_flakehub( Ok(url) } + +#[cfg(test)] +mod test { + use axum::{extract::Path, response::IntoResponse}; + + async fn version( + Path((org, project, version)): Path<(String, String, String)>, + ) -> axum::response::Response { + axum::Json(serde_json::json!({ + "project": project, + "pretty_download_url": format!("http://flakehub-localhost/f/{org}/{project}/{version}.tar.gz"), + })) + .into_response() + } + + async fn no_version(Path((org, project)): Path<(String, String)>) -> axum::response::Response { + axum::Json(serde_json::json!({ + "project": project, + "pretty_download_url": format!("http://flakehub-localhost/f/{org}/{project}/*.tar.gz"), + })) + .into_response() + } + + fn test_router() -> axum::Router { + let app = axum::Router::new() + .route( + "/version/:org/:project/:version", + axum::routing::get(version), + ) + .route("/f/:org/:project", axum::routing::get(no_version)); + + app + } + + #[tokio::test] + async fn nixpkgs_to_flakehub() { + let test_server = axum_test::TestServer::new(test_router().into_make_service()).unwrap(); + let server_addr = test_server.server_address(); + let server_url = server_addr.parse().unwrap(); + + let input_url = url::Url::parse("github:someorg/somerepo").unwrap(); + let tarball_url = super::convert_input_to_flakehub(&server_url, input_url) + .await + .ok() + .flatten() + .unwrap(); + assert_eq!(tarball_url.path(), "/f/someorg/somerepo/*.tar.gz"); + } + + #[tokio::test] + async fn nixpkgs_release_to_flakehub() { + let test_server = axum_test::TestServer::new(test_router().into_make_service()).unwrap(); + let server_addr = test_server.server_address(); + let server_url = server_addr.parse().unwrap(); + + let input_url = url::Url::parse("github:nixos/nixpkgs/nixos-23.05").unwrap(); + let tarball_url = super::convert_input_to_flakehub(&server_url, input_url) + .await + .ok() + .flatten() + .unwrap(); + assert_eq!(tarball_url.path(), "/f/nixos/nixpkgs/0.2305.0.tar.gz"); + } + + #[tokio::test] + async fn test_flake1_convert() { + let test_server = axum_test::TestServer::new(test_router().into_make_service()).unwrap(); + let server_addr = test_server.server_address(); + let server_url = server_addr.parse().unwrap(); + + let convert = super::ConvertSubcommand { + flake_path: "".into(), + dry_run: true, + api_addr: server_url, + }; + let flake_contents = include_str!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/samples/flake1.test.nix" + )); + let flake_contents = flake_contents.to_string(); + let parsed = nixel::parse(flake_contents.clone()); + + let (new_flake_contents, flake_compat_input_name) = convert + .convert_inputs_to_flakehub(&parsed.expression, &flake_contents) + .await + .unwrap(); + let new_flake_contents = convert + .make_implicit_nixpkgs_explicit(&parsed.expression, &new_flake_contents) + .await + .unwrap(); + let new_flake_contents = convert + .fixup_flake_compat_input(&new_flake_contents, flake_compat_input_name.unwrap()) + .await + .unwrap(); + + assert!(new_flake_contents.contains( + r#"flake-compat.url = "http://flakehub-localhost/f/edolstra/flake-compat/*.tar.gz";"# + )); + assert!(new_flake_contents.contains("f/nixos/nixpkgs/0.2305.0.tar.gz")); + + let nixpkgs_url_lines: Vec<_> = new_flake_contents + .lines() + .filter(|line| { + line.contains("nixpkgs.url") && line.contains("f/nixos/nixpkgs/0.2305.0.tar.gz") + }) + .collect(); + let num_nixpkgs_url_lines = nixpkgs_url_lines.iter().count(); + assert_eq!(num_nixpkgs_url_lines, 1); + } +}