diff options
Diffstat (limited to '')
-rw-r--r-- | tests/archive.rs | 18 | ||||
-rw-r--r-- | tests/auth.rs | 147 | ||||
-rw-r--r-- | tests/auth_file.rs | 49 | ||||
-rw-r--r-- | tests/bind.rs | 10 | ||||
-rw-r--r-- | tests/cli.rs | 8 | ||||
-rw-r--r-- | tests/create_directories.rs | 29 | ||||
-rw-r--r-- | tests/fixtures/mod.rs | 72 | ||||
-rw-r--r-- | tests/header.rs | 14 | ||||
-rw-r--r-- | tests/navigation.rs | 38 | ||||
-rw-r--r-- | tests/qrcode.rs | 15 | ||||
-rw-r--r-- | tests/raw.rs | 51 | ||||
-rw-r--r-- | tests/readme.rs | 52 | ||||
-rw-r--r-- | tests/serve_request.rs | 67 | ||||
-rw-r--r-- | tests/tls.rs | 7 | ||||
-rw-r--r-- | tests/upload_files.rs | 16 | ||||
-rw-r--r-- | tests/utils/mod.rs | 4 | ||||
-rw-r--r-- | tests/webdav.rs | 150 |
17 files changed, 428 insertions, 319 deletions
diff --git a/tests/archive.rs b/tests/archive.rs index e6d0263..a8b5ed2 100644 --- a/tests/archive.rs +++ b/tests/archive.rs @@ -1,10 +1,10 @@ -mod fixtures; - -use fixtures::{server, Error, TestServer}; -use reqwest::StatusCode; +use reqwest::StatusCode; use rstest::rstest; -use select::document::Document; -use select::predicate::Text; +use select::{document::Document, predicate::Text}; + +mod fixtures; + +use crate::fixtures::{server, Error, TestServer}; #[rstest] fn archives_are_disabled(server: TestServer) -> Result<(), Error> { @@ -58,8 +58,10 @@ fn test_tar_archives(#[with(&["-g"])] server: TestServer) -> Result<(), Error> { } #[rstest] -#[case(server(&["--disable-indexing", "--enable-tar-gz", "--enable-tar", "--enable-zip"]))] -fn archives_are_disabled_when_indexing_disabled(#[case] server: TestServer) -> Result<(), Error> { +fn archives_are_disabled_when_indexing_disabled( + #[with(&["--disable-indexing", "--enable-tar-gz", "--enable-tar", "--enable-zip"])] + server: TestServer, +) -> Result<(), Error> { // Ensure the links to the archives are not present let body = reqwest::blocking::get(server.url())?; let parsed = Document::from_read(body)?; diff --git a/tests/auth.rs b/tests/auth.rs index 920f738..efa7827 100644 --- a/tests/auth.rs +++ b/tests/auth.rs @@ -1,31 +1,28 @@ -mod fixtures; - -use fixtures::{server, server_no_stderr, Error, FILES}; use pretty_assertions::assert_eq; -use reqwest::blocking::Client; -use reqwest::StatusCode; +use reqwest::{blocking::Client, StatusCode}; use rstest::rstest; -use select::document::Document; -use select::predicate::Text; - -#[rstest( - cli_auth_arg, client_username, client_password, - case("testuser:testpassword", "testuser", "testpassword"), - case( - "testuser:sha256:9f735e0df9a1ddc702bf0a1a7b83033f9f7153a00c29de82cedadc9957289b05", - "testuser", - "testpassword" - ), - case( - "testuser:sha512:e9e633097ab9ceb3e48ec3f70ee2beba41d05d5420efee5da85f97d97005727587fda33ef4ff2322088f4c79e8133cc9cd9f3512f4d3a303cbdb5bc585415a00", - "testuser", - "testpassword" - ), +use select::{document::Document, predicate::Text}; + +mod fixtures; + +use crate::fixtures::{server, Error, FILES}; + +#[rstest] +#[case("testuser:testpassword", "testuser", "testpassword")] +#[case( + "testuser:sha256:9f735e0df9a1ddc702bf0a1a7b83033f9f7153a00c29de82cedadc9957289b05", + "testuser", + "testpassword" +)] +#[case( + "testuser:sha512:e9e633097ab9ceb3e48ec3f70ee2beba41d05d5420efee5da85f97d97005727587fda33ef4ff2322088f4c79e8133cc9cd9f3512f4d3a303cbdb5bc585415a00", + "testuser", + "testpassword" )] fn auth_accepts( - cli_auth_arg: &str, - client_username: &str, - client_password: &str, + #[case] cli_auth_arg: &str, + #[case] client_username: &str, + #[case] client_password: &str, ) -> Result<(), Error> { let server = server(&["-a", cli_auth_arg]); let client = Client::new(); @@ -46,37 +43,35 @@ fn auth_accepts( Ok(()) } -#[rstest( - cli_auth_arg, client_username, client_password, - case("rightuser:rightpassword", "wronguser", "rightpassword"), - case( - "rightuser:sha256:314eee236177a721d0e58d3ca4ff01795cdcad1e8478ba8183a2e58d69c648c0", - "wronguser", - "rightpassword" - ), - case( - "rightuser:sha512:84ec4056571afeec9f5b59453305877e9a66c3f9a1d91733fde759b370c1d540b9dc58bfc88c5980ad2d020c3a8ee84f21314a180856f5a82ba29ecba29e2cab", - "wronguser", - "rightpassword" - ), - case("rightuser:rightpassword", "rightuser", "wrongpassword"), - case( - "rightuser:sha256:314eee236177a721d0e58d3ca4ff01795cdcad1e8478ba8183a2e58d69c648c0", - "rightuser", - "wrongpassword" - ), - case( - "rightuser:sha512:84ec4056571afeec9f5b59453305877e9a66c3f9a1d91733fde759b370c1d540b9dc58bfc88c5980ad2d020c3a8ee84f21314a180856f5a82ba29ecba29e2cab", - "rightuser", - "wrongpassword" - ), +#[rstest] +#[case("rightuser:rightpassword", "wronguser", "rightpassword")] +#[case( + "rightuser:sha256:314eee236177a721d0e58d3ca4ff01795cdcad1e8478ba8183a2e58d69c648c0", + "wronguser", + "rightpassword" +)] +#[case( + "rightuser:sha512:84ec4056571afeec9f5b59453305877e9a66c3f9a1d91733fde759b370c1d540b9dc58bfc88c5980ad2d020c3a8ee84f21314a180856f5a82ba29ecba29e2cab", + "wronguser", + "rightpassword" +)] +#[case("rightuser:rightpassword", "rightuser", "wrongpassword")] +#[case( + "rightuser:sha256:314eee236177a721d0e58d3ca4ff01795cdcad1e8478ba8183a2e58d69c648c0", + "rightuser", + "wrongpassword" +)] +#[case( + "rightuser:sha512:84ec4056571afeec9f5b59453305877e9a66c3f9a1d91733fde759b370c1d540b9dc58bfc88c5980ad2d020c3a8ee84f21314a180856f5a82ba29ecba29e2cab", + "rightuser", + "wrongpassword" )] fn auth_rejects( - cli_auth_arg: &str, - client_username: &str, - client_password: &str, + #[case] cli_auth_arg: &str, + #[case] client_username: &str, + #[case] client_password: &str, ) -> Result<(), Error> { - let server = server_no_stderr(&["-a", cli_auth_arg]); + let server = server(&["-a", cli_auth_arg]); let client = Client::new(); let status = client .get(server.url()) @@ -106,17 +101,17 @@ static ACCOUNTS: &[&str] = &[ // pwd5 ]; -#[rstest( - username, - password, - case("usr0", "pwd0"), - case("usr1", "pwd1"), - case("usr2", "pwd2"), - case("usr3", "pwd3"), - case("usr4", "pwd4"), - case("usr5", "pwd5") -)] -fn auth_multiple_accounts_pass(username: &str, password: &str) -> Result<(), Error> { +#[rstest] +#[case("usr0", "pwd0")] +#[case("usr1", "pwd1")] +#[case("usr2", "pwd2")] +#[case("usr3", "pwd3")] +#[case("usr4", "pwd4")] +#[case("usr5", "pwd5")] +fn auth_multiple_accounts_pass( + #[case] username: &str, + #[case] password: &str, +) -> Result<(), Error> { let server = server(ACCOUNTS); let client = Client::new(); @@ -139,7 +134,7 @@ fn auth_multiple_accounts_pass(username: &str, password: &str) -> Result<(), Err #[rstest] fn auth_multiple_accounts_wrong_username() -> Result<(), Error> { - let server = server_no_stderr(ACCOUNTS); + let server = server(ACCOUNTS); let client = Client::new(); let status = client @@ -153,18 +148,18 @@ fn auth_multiple_accounts_wrong_username() -> Result<(), Error> { Ok(()) } -#[rstest( - username, - password, - case("usr0", "pwd5"), - case("usr1", "pwd4"), - case("usr2", "pwd3"), - case("usr3", "pwd2"), - case("usr4", "pwd1"), - case("usr5", "pwd0") -)] -fn auth_multiple_accounts_wrong_password(username: &str, password: &str) -> Result<(), Error> { - let server = server_no_stderr(ACCOUNTS); +#[rstest] +#[case("usr0", "pwd5")] +#[case("usr1", "pwd4")] +#[case("usr2", "pwd3")] +#[case("usr3", "pwd2")] +#[case("usr4", "pwd1")] +#[case("usr5", "pwd0")] +fn auth_multiple_accounts_wrong_password( + #[case] username: &str, + #[case] password: &str, +) -> Result<(), Error> { + let server = server(ACCOUNTS); let client = Client::new(); let status = client diff --git a/tests/auth_file.rs b/tests/auth_file.rs index ddc9e25..5632d46 100644 --- a/tests/auth_file.rs +++ b/tests/auth_file.rs @@ -1,26 +1,20 @@ +use reqwest::{blocking::Client, StatusCode}; +use rstest::rstest; +use select::{document::Document, predicate::Text}; + mod fixtures; -use fixtures::{server, server_no_stderr, Error, FILES}; -use http::StatusCode; -use reqwest::blocking::Client; -use rstest::rstest; -use select::document::Document; -use select::predicate::Text; +use crate::fixtures::{server, Error, TestServer, FILES}; -#[rstest( - cli_auth_file_arg, - client_username, - client_password, - case("tests/data/auth1.txt", "joe", "123"), - case("tests/data/auth1.txt", "bob", "123"), - case("tests/data/auth1.txt", "bill", "") -)] +#[rstest] +#[case("joe", "123")] +#[case("bob", "123")] +#[case("bill", "")] fn auth_file_accepts( - cli_auth_file_arg: &str, - client_username: &str, - client_password: &str, + #[with(&["--auth-file", "tests/data/auth1.txt"])] server: TestServer, + #[case] client_username: &str, + #[case] client_password: &str, ) -> Result<(), Error> { - let server = server(&["--auth-file", cli_auth_file_arg]); let client = Client::new(); let response = client .get(server.url()) @@ -39,20 +33,15 @@ fn auth_file_accepts( Ok(()) } -#[rstest( - cli_auth_file_arg, - client_username, - client_password, - case("tests/data/auth1.txt", "joe", "wrongpassword"), - case("tests/data/auth1.txt", "bob", ""), - case("tests/data/auth1.txt", "nonexistentuser", "wrongpassword") -)] +#[rstest] +#[case("joe", "wrongpassword")] +#[case("bob", "")] +#[case("nonexistentuser", "wrongpassword")] fn auth_file_rejects( - cli_auth_file_arg: &str, - client_username: &str, - client_password: &str, + #[with(&["--auth-file", "tests/data/auth1.txt"])] server: TestServer, + #[case] client_username: &str, + #[case] client_password: &str, ) -> Result<(), Error> { - let server = server_no_stderr(&["--auth-file", cli_auth_file_arg]); let client = Client::new(); let status = client .get(server.url()) diff --git a/tests/bind.rs b/tests/bind.rs index 0fa914e..e6c448a 100644 --- a/tests/bind.rs +++ b/tests/bind.rs @@ -1,12 +1,14 @@ -mod fixtures; +use std::io::{BufRead, BufReader}; +use std::process::{Command, Stdio}; use assert_cmd::prelude::*; use assert_fs::fixture::TempDir; -use fixtures::{port, server, tmpdir, Error, TestServer}; use regex::Regex; use rstest::rstest; -use std::io::{BufRead, BufReader}; -use std::process::{Command, Stdio}; + +mod fixtures; + +use crate::fixtures::{port, server, tmpdir, Error, TestServer}; #[rstest] #[case(&["-i", "12.123.234.12"])] diff --git a/tests/cli.rs b/tests/cli.rs index 8ec03a8..7c53698 100644 --- a/tests/cli.rs +++ b/tests/cli.rs @@ -1,10 +1,12 @@ -mod fixtures; +use std::process::Command; use assert_cmd::prelude::*; use clap::{crate_name, crate_version, ValueEnum}; use clap_complete::Shell; -use fixtures::Error; -use std::process::Command; + +mod fixtures; + +use crate::fixtures::Error; #[test] /// Show help and exit. diff --git a/tests/create_directories.rs b/tests/create_directories.rs index 380c796..bd9259f 100644 --- a/tests/create_directories.rs +++ b/tests/create_directories.rs @@ -1,14 +1,13 @@ -mod fixtures; - -use fixtures::{server, Error, TestServer, DIRECTORIES}; use reqwest::blocking::{multipart, Client}; use rstest::rstest; -use select::document::Document; -use select::predicate::{Attr, Text}; -#[cfg(unix)] -use std::os::unix::fs::symlink as symlink_dir; -#[cfg(windows)] -use std::os::windows::fs::symlink_dir; +use select::{ + document::Document, + predicate::{Attr, Text}, +}; + +mod fixtures; + +use crate::fixtures::{server, Error, TestServer, DIRECTORY_SYMLINK}; /// This should work because the flags for uploading files and creating directories /// are set, and the directory name and path are valid. @@ -96,20 +95,14 @@ fn creating_directories_is_prevented(server: TestServer) -> Result<(), Error> { fn creating_directories_through_symlinks_is_prevented( #[with(&["--upload-files", "--mkdir", "--no-symlinks"])] server: TestServer, ) -> Result<(), Error> { - // Make symlinks - let symlink_directory_str = "symlink"; - let symlink_directory = server.path().join(symlink_directory_str); - let symlinked_direcotry = server.path().join(DIRECTORIES[0]); - symlink_dir(symlinked_direcotry, symlink_directory).unwrap(); - // Before attempting to create, ensure the symlink does not exist. let body = reqwest::blocking::get(server.url())?.error_for_status()?; let parsed = Document::from_read(body)?; - assert!(parsed.find(Text).all(|x| x.text() != symlink_directory_str)); + assert!(parsed.find(Text).all(|x| x.text() != DIRECTORY_SYMLINK)); // Attempt to perform directory creation. let form = multipart::Form::new(); - let part = multipart::Part::text(symlink_directory_str); + let part = multipart::Part::text(DIRECTORY_SYMLINK); let form = form.part("mkdir", part); // This should fail @@ -117,7 +110,7 @@ fn creating_directories_through_symlinks_is_prevented( .post( server .url() - .join(format!("/upload?path=/{symlink_directory_str}").as_str())? + .join(format!("/upload?path=/{DIRECTORY_SYMLINK}").as_str())? ) .multipart(form) .send()? diff --git a/tests/fixtures/mod.rs b/tests/fixtures/mod.rs index 96b8002..f2869b7 100644 --- a/tests/fixtures/mod.rs +++ b/tests/fixtures/mod.rs @@ -1,18 +1,18 @@ +use std::process::{Child, Command, Stdio}; +use std::thread::sleep; +use std::time::{Duration, Instant}; + use assert_cmd::prelude::*; use assert_fs::fixture::TempDir; use assert_fs::prelude::*; use port_check::free_local_port; use reqwest::Url; use rstest::fixture; -use std::process::{Child, Command, Stdio}; -use std::thread::sleep; -use std::time::{Duration, Instant}; /// Error type used by tests pub type Error = Box<dyn std::error::Error>; /// File names for testing purpose -#[allow(dead_code)] pub static FILES: &[&str] = &[ "test.txt", "test.html", @@ -31,25 +31,29 @@ pub static FILES: &[&str] = &[ ]; /// Hidden files for testing purpose -#[allow(dead_code)] pub static HIDDEN_FILES: &[&str] = &[".hidden_file1", ".hidden_file2"]; /// Directory names for testing purpose -#[allow(dead_code)] pub static DIRECTORIES: &[&str] = &["dira/", "dirb/", "dirc/"]; /// Hidden directories for testing purpose -#[allow(dead_code)] pub static HIDDEN_DIRECTORIES: &[&str] = &[".hidden_dir1/", ".hidden_dir2/"]; /// Name of a deeply nested file -#[allow(dead_code)] pub static DEEPLY_NESTED_FILE: &str = "very/deeply/nested/test.rs"; +/// Name of a symlink pointing to a directory +pub static DIRECTORY_SYMLINK: &str = "dir_symlink/"; + +/// Name of a symlink pointing to a file +pub static FILE_SYMLINK: &str = "file_symlink"; + +/// Name of a symlink pointing to a path that doesn't exist +pub static BROKEN_SYMLINK: &str = "broken_symlink"; + /// Test fixture which creates a temporary directory with a few files and directories inside. /// The directories also contain files. #[fixture] -#[allow(dead_code)] pub fn tmpdir() -> TempDir { let tmpdir = assert_fs::TempDir::new().expect("Couldn't create a temp dir for tests"); let mut files = FILES.to_vec(); @@ -76,12 +80,27 @@ pub fn tmpdir() -> TempDir { .child(DEEPLY_NESTED_FILE) .write_str("File in a deeply nested directory.") .expect("Couldn't write to file"); + + tmpdir + .child(DIRECTORY_SYMLINK.strip_suffix("/").unwrap()) + .symlink_to_dir(DIRECTORIES[0].strip_suffix("/").unwrap()) + .expect("Couldn't create symlink to dir"); + + tmpdir + .child(FILE_SYMLINK) + .symlink_to_file(FILES[0]) + .expect("Couldn't create symlink to file"); + + tmpdir + .child(BROKEN_SYMLINK) + .symlink_to_file("broken_symlink") + .expect("Couldn't create broken symlink"); + tmpdir } /// Get a free port. #[fixture] -#[allow(dead_code)] pub fn port() -> u16 { free_local_port().expect("Couldn't find a free local port") } @@ -89,7 +108,6 @@ pub fn port() -> u16 { /// Run miniserve as a server; Start with a temporary directory, a free port and some /// optional arguments then wait for a while for the server setup to complete. #[fixture] -#[allow(dead_code)] pub fn server<I>(#[default(&[] as &[&str])] args: I) -> TestServer where I: IntoIterator + Clone, @@ -103,35 +121,8 @@ where .arg("-p") .arg(port.to_string()) .args(args.clone()) - .stdout(Stdio::null()) - .spawn() - .expect("Couldn't run test binary"); - let is_tls = args - .into_iter() - .any(|x| x.as_ref().to_str().unwrap().contains("tls")); - - wait_for_port(port); - TestServer::new(port, tmpdir, child, is_tls) -} - -/// Same as `server()` but ignore stderr -#[fixture] -#[allow(dead_code)] -pub fn server_no_stderr<I>(#[default(&[] as &[&str])] args: I) -> TestServer -where - I: IntoIterator + Clone, - I::Item: AsRef<std::ffi::OsStr>, -{ - let port = port(); - let tmpdir = tmpdir(); - let child = Command::cargo_bin("miniserve") - .expect("Couldn't find test binary") - .arg(tmpdir.path()) - .arg("-p") - .arg(port.to_string()) - .args(args.clone()) - .stdout(Stdio::null()) - .stderr(Stdio::null()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) .spawn() .expect("Couldn't run test binary"); let is_tls = args @@ -155,7 +146,6 @@ fn wait_for_port(port: u16) { } } -#[allow(dead_code)] pub struct TestServer { port: u16, tmpdir: TempDir, diff --git a/tests/header.rs b/tests/header.rs index 4ac38b1..443f2ba 100644 --- a/tests/header.rs +++ b/tests/header.rs @@ -1,13 +1,13 @@ +use rstest::rstest; + mod fixtures; -use fixtures::{server, Error}; -use rstest::rstest; +use crate::fixtures::{server, Error}; -#[rstest(headers, - case(vec!["x-info: 123".to_string()]), - case(vec!["x-info1: 123".to_string(), "x-info2: 345".to_string()]) -)] -fn custom_header_set(headers: Vec<String>) -> Result<(), Error> { +#[rstest] +#[case(vec!["x-info: 123".to_string()])] +#[case(vec!["x-info1: 123".to_string(), "x-info2: 345".to_string()])] +fn custom_header_set(#[case] headers: Vec<String>) -> Result<(), Error> { let server = server(headers.iter().flat_map(|h| vec!["--header", h])); let resp = reqwest::blocking::get(server.url())?; diff --git a/tests/navigation.rs b/tests/navigation.rs index 8c21beb..1bd8e81 100644 --- a/tests/navigation.rs +++ b/tests/navigation.rs @@ -1,24 +1,26 @@ -mod fixtures; -mod utils; +use std::process::{Command, Stdio}; -use fixtures::{server, Error, TestServer, DEEPLY_NESTED_FILE, DIRECTORIES}; use pretty_assertions::{assert_eq, assert_ne}; use rstest::rstest; use select::document::Document; -use std::process::{Command, Stdio}; -use utils::get_link_from_text; -use utils::get_link_hrefs_with_prefix; - -#[rstest( - input, - expected, - case("", "/"), - case("/dira", "/dira/"), - case("/dirb/", "/dirb/"), - case("/very/deeply/nested", "/very/deeply/nested/") -)] + +mod fixtures; +mod utils; + +use crate::fixtures::{server, Error, TestServer, DEEPLY_NESTED_FILE, DIRECTORIES}; +use crate::utils::{get_link_from_text, get_link_hrefs_with_prefix}; + +#[rstest] +#[case("", "/")] +#[case("/dira", "/dira/")] +#[case("/dirb/", "/dirb/")] +#[case("/very/deeply/nested", "/very/deeply/nested/")] /// Directories get a trailing slash. -fn index_gets_trailing_slash(server: TestServer, input: &str, expected: &str) -> Result<(), Error> { +fn index_gets_trailing_slash( + server: TestServer, + #[case] input: &str, + #[case] expected: &str, +) -> Result<(), Error> { let resp = reqwest::blocking::get(server.url().join(input)?)?; assert!(resp.url().as_str().ends_with(expected)); @@ -52,11 +54,11 @@ fn can_navigate_into_dirs_and_back(server: TestServer) -> Result<(), Error> { let initial_parsed = Document::from_read(initial_body)?; for &directory in DIRECTORIES { let dir_elem = get_link_from_text(&initial_parsed, directory).expect("Dir not found."); - let body = reqwest::blocking::get(&format!("{base_url}{dir_elem}"))?.error_for_status()?; + let body = reqwest::blocking::get(format!("{base_url}{dir_elem}"))?.error_for_status()?; let parsed = Document::from_read(body)?; let back_link = get_link_from_text(&parsed, "Parent directory").expect("Back link not found."); - let resp = reqwest::blocking::get(&format!("{base_url}{back_link}"))?; + let resp = reqwest::blocking::get(format!("{base_url}{back_link}"))?; // Now check that we can actually get back to the original location we came from using the // link. diff --git a/tests/qrcode.rs b/tests/qrcode.rs index 48fa8c8..6951d7a 100644 --- a/tests/qrcode.rs +++ b/tests/qrcode.rs @@ -1,14 +1,15 @@ -mod fixtures; +use std::process::{Command, Stdio}; +use std::thread::sleep; +use std::time::Duration; use assert_cmd::prelude::CommandCargoExt; use assert_fs::TempDir; -use fixtures::{port, server, tmpdir, Error, TestServer}; use rstest::rstest; -use select::document::Document; -use select::predicate::Attr; -use std::process::{Command, Stdio}; -use std::thread::sleep; -use std::time::Duration; +use select::{document::Document, predicate::Attr}; + +mod fixtures; + +use crate::fixtures::{port, server, tmpdir, Error, TestServer}; #[rstest] fn webpage_hides_qrcode_when_disabled(server: TestServer) -> Result<(), Error> { diff --git a/tests/raw.rs b/tests/raw.rs index 95100d2..051c3e3 100644 --- a/tests/raw.rs +++ b/tests/raw.rs @@ -1,30 +1,27 @@ -mod fixtures; -mod utils; - -use crate::fixtures::TestServer; -use fixtures::{server, Error}; use pretty_assertions::assert_eq; use reqwest::blocking::Client; use rstest::rstest; -use select::document::Document; -use select::predicate::Class; -use select::predicate::Name; +use select::{ + document::Document, + predicate::{Class, Name}, +}; + +mod fixtures; + +use crate::fixtures::{server, Error, TestServer}; /// The footer displays the correct wget command to download the folder recursively // This test can't test all aspects of the wget footer, // a more detailed unit test is available -#[rstest( - depth, - dir, - case(0, ""), - case(1, "dira/"), - case(2, "very/deeply/"), - case(3, "very/deeply/nested/") -)] +#[rstest] +#[case(0, "")] +#[case(1, "dira/")] +#[case(2, "very/deeply/")] +#[case(3, "very/deeply/nested/")] fn ui_displays_wget_element( - depth: u8, - dir: &str, - #[with(&["-W"])] server: TestServer, + #[case] depth: u8, + #[case] dir: &str, + #[with(&["--show-wget-footer"])] server: TestServer, ) -> Result<(), Error> { let client = Client::new(); @@ -62,16 +59,14 @@ fn ui_displays_wget_element( } /// All hrefs in raw mode are links to directories or files & directories end with ?raw=true -#[rstest( - dir, - case(""), - case("very/"), - case("very/deeply/"), - case("very/deeply/nested/") -)] +#[rstest] +#[case("")] +#[case("very/")] +#[case("very/deeply/")] +#[case("very/deeply/nested/")] fn raw_mode_links_to_directories_end_with_raw_true( - dir: &str, - #[with(&["-W"])] server: TestServer, + #[case] dir: &str, + #[with(&["--show-wget-footer"])] server: TestServer, ) -> Result<(), Error> { fn verify_a_tags(parsed: Document) { // Ensure all links end with ?raw=true or are files diff --git a/tests/readme.rs b/tests/readme.rs index c8138b4..cafff3d 100644 --- a/tests/readme.rs +++ b/tests/readme.rs @@ -1,12 +1,14 @@ -mod fixtures; +use std::fs::{remove_file, File}; +use std::io::Write; +use std::path::PathBuf; -use fixtures::{server, Error, TestServer, DIRECTORIES, FILES}; use rstest::rstest; use select::predicate::Attr; use select::{document::Document, node::Node}; -use std::fs::{remove_file, File}; -use std::io::Write; -use std::path::PathBuf; + +mod fixtures; + +use fixtures::{server, Error, TestServer, DIRECTORIES, FILES}; fn write_readme_contents(path: PathBuf, filename: &str) -> PathBuf { let readme_path = path.join(filename); @@ -67,17 +69,15 @@ fn no_readme_contents(server: TestServer) -> Result<(), Error> { } /// Show readme contents when told to if there is a readme file in the root -#[rstest( - readme_name, - case("Readme.md"), - case("readme.md"), - case("README.md"), - case("README.MD"), - case("ReAdMe.Md") -)] +#[rstest] +#[case("Readme.md")] +#[case("readme.md")] +#[case("README.md")] +#[case("README.MD")] +#[case("ReAdMe.Md")] fn show_root_readme_contents( #[with(&["--readme"])] server: TestServer, - readme_name: &str, + #[case] readme_name: &str, ) -> Result<(), Error> { let readme_path = write_readme_contents(server.path().to_path_buf(), readme_name); let body = reqwest::blocking::get(server.url())?.error_for_status()?; @@ -94,21 +94,19 @@ fn show_root_readme_contents( } /// Show readme contents when told to if there is a readme file in any of the directories -#[rstest( - readme_name, - case("Readme.md"), - case("readme.md"), - case("README.md"), - case("README.MD"), - case("ReAdMe.Md"), - case("Readme.txt"), - case("README.txt"), - case("README"), - case("ReAdMe") -)] +#[rstest] +#[case("Readme.md")] +#[case("readme.md")] +#[case("README.md")] +#[case("README.MD")] +#[case("ReAdMe.Md")] +#[case("Readme.txt")] +#[case("README.txt")] +#[case("README")] +#[case("ReAdMe")] fn show_nested_readme_contents( #[with(&["--readme"])] server: TestServer, - readme_name: &str, + #[case] readme_name: &str, ) -> Result<(), Error> { for dir in DIRECTORIES { let readme_path = write_readme_contents(server.path().join(dir), readme_name); diff --git a/tests/serve_request.rs b/tests/serve_request.rs index b7359c3..f840efd 100644 --- a/tests/serve_request.rs +++ b/tests/serve_request.rs @@ -1,23 +1,21 @@ -mod fixtures; +use std::process::{Command, Stdio}; +use std::thread::sleep; +use std::time::Duration; use assert_cmd::prelude::*; use assert_fs::fixture::TempDir; -use fixtures::{ - port, server, server_no_stderr, tmpdir, Error, TestServer, DIRECTORIES, FILES, - HIDDEN_DIRECTORIES, HIDDEN_FILES, -}; -use http::StatusCode; +use fixtures::BROKEN_SYMLINK; use regex::Regex; +use reqwest::StatusCode; use rstest::rstest; use select::{document::Document, node::Node, predicate::Attr}; -use std::process::{Command, Stdio}; -use std::thread::sleep; -use std::time::Duration; -#[cfg(unix)] -use std::os::unix::fs::{symlink as symlink_dir, symlink as symlink_file}; -#[cfg(windows)] -use std::os::windows::fs::{symlink_dir, symlink_file}; +mod fixtures; + +use crate::fixtures::{ + port, server, tmpdir, Error, TestServer, DIRECTORIES, DIRECTORY_SYMLINK, FILES, FILE_SYMLINK, + HIDDEN_DIRECTORIES, HIDDEN_FILES, +}; #[rstest] fn serves_requests_with_no_options(tmpdir: TempDir) -> Result<(), Error> { @@ -131,23 +129,10 @@ fn serves_requests_symlinks( #[case] show_symlink_info: bool, #[case] server: TestServer, ) -> Result<(), Error> { - let file = "symlink-file.html"; - let dir = "symlink-dir/"; - let broken = "symlink broken"; - - // Set up some basic symlinks: - // to dir, to file, to non-existent location - let orig = DIRECTORIES[0].strip_suffix('/').unwrap(); - let link = server.path().join(dir.strip_suffix('/').unwrap()); - symlink_dir(orig, link).expect("Couldn't create symlink"); - symlink_file(FILES[0], server.path().join(file)).expect("Couldn't create symlink"); - symlink_file("should-not-exist.xxx", server.path().join(broken)) - .expect("Couldn't create symlink"); - let body = reqwest::blocking::get(server.url())?.error_for_status()?; let parsed = Document::from_read(body)?; - for &entry in &[file, dir] { + for &entry in &[FILE_SYMLINK, DIRECTORY_SYMLINK] { let status = reqwest::blocking::get(server.url().join(entry)?)?.status(); // We expect a 404 here for when `no_symlinks` is `true`. if no_symlinks { @@ -161,6 +146,7 @@ fn serves_requests_symlinks( .next(); // If symlinks are deactivated, none should be shown in the listing. + dbg!(&node); assert_eq!(node.is_none(), no_symlinks); if node.is_some() && show_symlink_info { assert_eq!(node.unwrap().attr("class").unwrap(), "symlink"); @@ -186,7 +172,10 @@ fn serves_requests_symlinks( assert_eq!(node.unwrap().attr("class").unwrap(), "file"); } } - assert!(parsed.find(|x: &Node| x.text() == broken).next().is_none()); + assert!(parsed + .find(|x: &Node| x.text() == BROKEN_SYMLINK) + .next() + .is_none()); Ok(()) } @@ -240,8 +229,8 @@ fn serves_requests_custom_index_notice(tmpdir: TempDir, port: u16) -> Result<(), } #[rstest] -#[case(server_no_stderr(&["--index", FILES[0]]))] -#[case(server_no_stderr(&["--index", "does-not-exist.html"]))] +#[case(server(&["--index", FILES[0]]))] +#[case(server(&["--index", "does-not-exist.html"]))] fn index_fallback_to_listing(#[case] server: TestServer) -> Result<(), Error> { // If index file is not found, show directory listing instead both cases should return `Ok` reqwest::blocking::get(server.url())?.error_for_status()?; @@ -250,9 +239,9 @@ fn index_fallback_to_listing(#[case] server: TestServer) -> Result<(), Error> { } #[rstest] -#[case(server_no_stderr(&["--spa", "--index", FILES[0]]), "/")] -#[case(server_no_stderr(&["--spa", "--index", FILES[0]]), "/spa-route")] -#[case(server_no_stderr(&["--index", FILES[0]]), "/")] +#[case(server(&["--spa", "--index", FILES[0]]), "/")] +#[case(server(&["--spa", "--index", FILES[0]]), "/spa-route")] +#[case(server(&["--index", FILES[0]]), "/")] fn serve_index_instead_of_404_in_spa_mode( #[case] server: TestServer, #[case] url: &str, @@ -268,9 +257,9 @@ fn serve_index_instead_of_404_in_spa_mode( } #[rstest] -#[case(server_no_stderr(&["--pretty-urls", "--index", FILES[1]]), "/")] -#[case(server_no_stderr(&["--pretty-urls", "--index", FILES[1]]), "test.html")] -#[case(server_no_stderr(&["--pretty-urls", "--index", FILES[1]]), "test")] +#[case(server(&["--pretty-urls", "--index", FILES[1]]), "/")] +#[case(server(&["--pretty-urls", "--index", FILES[1]]), "test.html")] +#[case(server(&["--pretty-urls", "--index", FILES[1]]), "test")] fn serve_file_instead_of_404_in_pretty_urls_mode( #[case] server: TestServer, #[case] url: &str, @@ -301,9 +290,9 @@ fn serves_requests_with_route_prefix(#[case] server: TestServer) -> Result<(), E } #[rstest] -#[case(server_no_stderr(&[] as &[&str]), "/[a-f0-9]+")] -#[case(server_no_stderr(&["--random-route"]), "/[a-f0-9]+")] -#[case(server_no_stderr(&["--route-prefix", "foobar"]), "/foobar/[a-f0-9]+")] +#[case(server(&[] as &[&str]), "/__miniserve_internal/[a-z.]+")] +#[case(server(&["--random-route"]), "/__miniserve_internal/[a-z.]+")] +#[case(server(&["--route-prefix", "foobar"]), "/foobar/__miniserve_internal/[a-z.]+")] fn serves_requests_static_file_check( #[case] server: TestServer, #[case] static_file_pattern: String, diff --git a/tests/tls.rs b/tests/tls.rs index 7750c82..9cc441c 100644 --- a/tests/tls.rs +++ b/tests/tls.rs @@ -1,12 +1,13 @@ -mod fixtures; - use assert_cmd::Command; -use fixtures::{server, Error, TestServer, FILES}; use predicates::str::contains; use reqwest::blocking::ClientBuilder; use rstest::rstest; use select::{document::Document, node::Node}; +mod fixtures; + +use crate::fixtures::{server, Error, TestServer, FILES}; + /// Can start the server with TLS and receive encrypted responses. #[rstest] #[case(server(&[ diff --git a/tests/upload_files.rs b/tests/upload_files.rs index 77a9dc3..5fdf2cf 100644 --- a/tests/upload_files.rs +++ b/tests/upload_files.rs @@ -1,13 +1,15 @@ -mod fixtures; +use std::fs::create_dir_all; +use std::path::Path; use assert_fs::fixture::TempDir; -use fixtures::{server, server_no_stderr, tmpdir, Error, TestServer}; use reqwest::blocking::{multipart, Client}; use rstest::rstest; use select::document::Document; use select::predicate::{Attr, Text}; -use std::fs::create_dir_all; -use std::path::Path; + +mod fixtures; + +use crate::fixtures::{server, tmpdir, Error, TestServer}; #[rstest] fn uploading_files_works(#[with(&["-u"])] server: TestServer) -> Result<(), Error> { @@ -85,8 +87,8 @@ fn uploading_files_is_prevented(server: TestServer) -> Result<(), Error> { /// This test runs the server with --allowed-upload-dir argument and /// checks that file upload to a different directory is actually prevented. #[rstest] -#[case(server_no_stderr(&["-u", "someDir"]))] -#[case(server_no_stderr(&["-u", "someDir/some_sub_dir"]))] +#[case(server(&["-u", "someDir"]))] +#[case(server(&["-u", "someDir/some_sub_dir"]))] fn uploading_files_is_restricted(#[case] server: TestServer) -> Result<(), Error> { let test_file_name = "uploaded test file.txt"; @@ -219,7 +221,7 @@ fn prevent_path_traversal_attacks( /// See https://github.com/svenstaro/miniserve/issues/466 #[rstest] #[case(server(&["-u"]), true)] -#[case(server_no_stderr(&["-u", "--no-symlinks"]), false)] +#[case(server(&["-u", "--no-symlinks"]), false)] fn upload_to_symlink_directory( #[case] server: TestServer, #[case] ok: bool, diff --git a/tests/utils/mod.rs b/tests/utils/mod.rs index baffc29..792b070 100644 --- a/tests/utils/mod.rs +++ b/tests/utils/mod.rs @@ -1,5 +1,3 @@ -#![allow(dead_code)] - use select::document::Document; use select::node::Node; use select::predicate::Name; @@ -26,5 +24,5 @@ pub fn get_link_hrefs_with_prefix(document: &Document, prefix: &str) -> Vec<Stri } } - return vec; + vec } diff --git a/tests/webdav.rs b/tests/webdav.rs new file mode 100644 index 0000000..09d04e9 --- /dev/null +++ b/tests/webdav.rs @@ -0,0 +1,150 @@ +use std::process::Command; + +use assert_cmd::prelude::*; +use assert_fs::TempDir; +use predicates::str::contains; +use reqwest::{blocking::Client, Method}; +use reqwest_dav::{ + list_cmd::{ListEntity, ListFile, ListFolder}, + ClientBuilder as DavClientBuilder, +}; +use rstest::rstest; + +mod fixtures; + +use crate::fixtures::{ + server, tmpdir, Error, TestServer, DIRECTORIES, DIRECTORY_SYMLINK, FILES, FILE_SYMLINK, + HIDDEN_DIRECTORIES, HIDDEN_FILES, +}; + +#[rstest] +#[case(server(&["--enable-webdav"]), true)] +#[case(server(&[] as &[&str]), false)] +fn webdav_flag_works( + #[case] server: TestServer, + #[case] should_respond: bool, +) -> Result<(), Error> { + let client = Client::new(); + let response = client + .request(Method::from_bytes(b"PROPFIND").unwrap(), server.url()) + .header("Depth", "1") + .send()?; + + assert_eq!(should_respond, response.status().is_success()); + + Ok(()) +} + +#[rstest] +fn webdav_advertised_in_options( + #[with(&["--enable-webdav"])] server: TestServer, +) -> Result<(), Error> { + let response = Client::new() + .request(Method::OPTIONS, server.url()) + .send()? + .error_for_status()?; + + let headers = response.headers(); + let allow = headers.get("allow").unwrap().to_str()?; + + assert!(allow.contains("OPTIONS") && allow.contains("PROPFIND")); + assert!(headers.get("dav").is_some()); + + Ok(()) +} + +fn list_webdav(url: url::Url, path: &str) -> Result<Vec<ListEntity>, reqwest_dav::Error> { + let client = DavClientBuilder::new().set_host(url.to_string()).build()?; + + let rt = tokio::runtime::Runtime::new().unwrap(); + + rt.block_on(async { client.list(path, reqwest_dav::Depth::Number(1)).await }) +} + +#[rstest] +#[case(server(&["--enable-webdav"]), false)] +#[case(server(&["--enable-webdav", "--hidden"]), true)] +fn webdav_respects_hidden_flag( + #[case] server: TestServer, + #[case] hidden_should_show: bool, +) -> Result<(), Error> { + let list = list_webdav(server.url(), "/")?; + + assert_eq!( + hidden_should_show, + list.iter().any(|el| + matches!(el, ListEntity::File(ListFile { href, .. }) if href.contains(HIDDEN_FILES[0])) + ) + ); + + assert_eq!( + hidden_should_show, + list.iter().any(|el| + matches!(el, ListEntity::Folder(ListFolder { href, .. }) if href.contains(HIDDEN_DIRECTORIES[0])) + ) + ); + + Ok(()) +} + +#[rstest] +#[case(server(&["--enable-webdav"]), true)] +#[should_panic] +#[case(server(&["--enable-webdav", "--no-symlinks"]), false)] +fn webdav_respects_no_symlink_flag(#[case] server: TestServer, #[case] symlinks_should_show: bool) { + let list = list_webdav(server.url(), "/").unwrap(); + + assert_eq!( + symlinks_should_show, + list.iter().any(|el| + matches!(el, ListEntity::File(ListFile { href, .. }) if href.contains(FILE_SYMLINK)) + ), + ); + + assert_eq!( + symlinks_should_show, + list.iter().any(|el| + matches!(el, ListEntity::Folder(ListFolder { href, .. }) if href.contains(DIRECTORY_SYMLINK)) + ), + ); + + let list_linked = list_webdav(server.url(), &format!("/{}", DIRECTORY_SYMLINK)); + + assert_eq!(symlinks_should_show, list_linked.is_ok()); +} + +#[rstest] +fn webdav_works_with_route_prefix( + #[with(&["--enable-webdav", "--route-prefix", "test-prefix"])] server: TestServer, +) -> Result<(), Error> { + let prefixed_list = list_webdav(server.url().join("test-prefix")?, "/")?; + + assert!( + prefixed_list.iter().any(|el| + matches!(el, ListEntity::Folder(ListFolder { href, .. }) if href.contains(DIRECTORIES[0])) + ) + ); + + let root_list = list_webdav(server.url(), "/"); + + assert!(root_list.is_err()); + + Ok(()) +} + +// timeout is used in case the binary does not exit as expected and starts waiting for requests +#[rstest] +#[timeout(std::time::Duration::from_secs(1))] +fn webdav_single_file_refuses_starting(tmpdir: TempDir) { + Command::cargo_bin("miniserve") + .unwrap() + .current_dir(tmpdir.path()) + .arg(FILES[0]) + .arg("--enable-webdav") + .assert() + .failure() + .stderr(contains(format!( + "Error: The --enable-webdav option was provided, but the serve path '{}' is a file", + FILES[0] + ))); +} |