sigoden
3 years ago
committed by
GitHub
21 changed files with 2637 additions and 94 deletions
@ -0,0 +1,61 @@ |
|||||||
|
mod fixtures; |
||||||
|
mod utils; |
||||||
|
|
||||||
|
use fixtures::{server, Error, TestServer}; |
||||||
|
use rstest::rstest; |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn default_not_allow_upload(server: TestServer) -> Result<(), Error> { |
||||||
|
let url = format!("{}file1", server.url()); |
||||||
|
let resp = fetch!(b"PUT", &url).body(b"abc".to_vec()).send()?; |
||||||
|
assert_eq!(resp.status(), 403); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn default_not_allow_delete(server: TestServer) -> Result<(), Error> { |
||||||
|
let url = format!("{}test.html", server.url()); |
||||||
|
let resp = fetch!(b"DELETE", &url).send()?; |
||||||
|
assert_eq!(resp.status(), 403); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn default_not_exist_dir(server: TestServer) -> Result<(), Error> { |
||||||
|
let resp = reqwest::blocking::get(format!("{}404/", server.url()))?; |
||||||
|
assert_eq!(resp.status(), 404); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn allow_upload_not_exist_dir( |
||||||
|
#[with(&["--allow-upload"])] server: TestServer, |
||||||
|
) -> Result<(), Error> { |
||||||
|
let resp = reqwest::blocking::get(format!("{}404/", server.url()))?; |
||||||
|
assert_eq!(resp.status(), 200); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn allow_upload_no_override(#[with(&["--allow-upload"])] server: TestServer) -> Result<(), Error> { |
||||||
|
let url = format!("{}index.html", server.url()); |
||||||
|
let resp = fetch!(b"PUT", &url).body(b"abc".to_vec()).send()?; |
||||||
|
assert_eq!(resp.status(), 403); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn allow_delete_no_override(#[with(&["--allow-delete"])] server: TestServer) -> Result<(), Error> { |
||||||
|
let url = format!("{}index.html", server.url()); |
||||||
|
let resp = fetch!(b"PUT", &url).body(b"abc".to_vec()).send()?; |
||||||
|
assert_eq!(resp.status(), 403); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn allow_upload_delete_can_override(#[with(&["-A"])] server: TestServer) -> Result<(), Error> { |
||||||
|
let url = format!("{}index.html", server.url()); |
||||||
|
let resp = fetch!(b"PUT", &url).body(b"abc".to_vec()).send()?; |
||||||
|
assert_eq!(resp.status(), 201); |
||||||
|
Ok(()) |
||||||
|
} |
@ -0,0 +1,38 @@ |
|||||||
|
mod fixtures; |
||||||
|
mod utils; |
||||||
|
|
||||||
|
use diqwest::blocking::WithDigestAuth; |
||||||
|
use fixtures::{server, Error, TestServer}; |
||||||
|
use rstest::rstest; |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn no_auth(#[with(&["--auth", "user:pass", "-A"])] server: TestServer) -> Result<(), Error> { |
||||||
|
let resp = reqwest::blocking::get(server.url())?; |
||||||
|
assert_eq!(resp.status(), 401); |
||||||
|
assert!(resp.headers().contains_key("www-authenticate")); |
||||||
|
let url = format!("{}file1", server.url()); |
||||||
|
let resp = fetch!(b"PUT", &url).body(b"abc".to_vec()).send()?; |
||||||
|
assert_eq!(resp.status(), 401); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn auth(#[with(&["--auth", "user:pass", "-A"])] server: TestServer) -> Result<(), Error> { |
||||||
|
let url = format!("{}file1", server.url()); |
||||||
|
let resp = fetch!(b"PUT", &url).body(b"abc".to_vec()).send()?; |
||||||
|
assert_eq!(resp.status(), 401); |
||||||
|
let resp = fetch!(b"PUT", &url) |
||||||
|
.body(b"abc".to_vec()) |
||||||
|
.send_with_digest_auth("user", "pass")?; |
||||||
|
assert_eq!(resp.status(), 201); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn auth_skip_access( |
||||||
|
#[with(&["--auth", "user:pass", "--no-auth-access"])] server: TestServer, |
||||||
|
) -> Result<(), Error> { |
||||||
|
let resp = reqwest::blocking::get(server.url())?; |
||||||
|
assert_eq!(resp.status(), 200); |
||||||
|
Ok(()) |
||||||
|
} |
@ -0,0 +1,80 @@ |
|||||||
|
mod fixtures; |
||||||
|
|
||||||
|
use fixtures::{port, server, tmpdir, Error, TestServer}; |
||||||
|
|
||||||
|
use assert_cmd::prelude::*; |
||||||
|
use assert_fs::fixture::TempDir; |
||||||
|
use regex::Regex; |
||||||
|
use rstest::rstest; |
||||||
|
use std::io::{BufRead, BufReader}; |
||||||
|
use std::process::{Command, Stdio}; |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
#[case(&["-b", "20.205.243.166"])] |
||||||
|
fn bind_fails(tmpdir: TempDir, port: u16, #[case] args: &[&str]) -> Result<(), Error> { |
||||||
|
Command::cargo_bin("duf")? |
||||||
|
.env("RUST_LOG", "false") |
||||||
|
.arg(tmpdir.path()) |
||||||
|
.arg("-p") |
||||||
|
.arg(port.to_string()) |
||||||
|
.args(args) |
||||||
|
.assert() |
||||||
|
.stderr(predicates::str::contains("creating server listener")) |
||||||
|
.failure(); |
||||||
|
|
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn bind_ipv4(server: TestServer) -> Result<(), Error> { |
||||||
|
assert!(reqwest::blocking::get(format!("http://127.0.0.1:{}", server.port()).as_str()).is_ok()); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn bind_ipv6(#[with(&["-b", "::"])] server: TestServer) -> Result<(), Error> { |
||||||
|
assert_eq!( |
||||||
|
reqwest::blocking::get(format!("http://127.0.0.1:{}", server.port()).as_str()).is_ok(), |
||||||
|
!cfg!(windows) |
||||||
|
); |
||||||
|
assert!(reqwest::blocking::get(format!("http://[::1]:{}", server.port()).as_str()).is_ok()); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
#[case(&[] as &[&str])] |
||||||
|
#[case(&["--path-prefix", "/prefix"])] |
||||||
|
fn validate_printed_urls(tmpdir: TempDir, port: u16, #[case] args: &[&str]) -> Result<(), Error> { |
||||||
|
let mut child = Command::cargo_bin("duf")? |
||||||
|
.env("RUST_LOG", "false") |
||||||
|
.arg(tmpdir.path()) |
||||||
|
.arg("-p") |
||||||
|
.arg(port.to_string()) |
||||||
|
.args(args) |
||||||
|
.stdout(Stdio::piped()) |
||||||
|
.spawn()?; |
||||||
|
|
||||||
|
// WARN assumes urls list is terminated by an empty line
|
||||||
|
let url_lines = BufReader::new(child.stdout.take().unwrap()) |
||||||
|
.lines() |
||||||
|
.map(|line| line.expect("Error reading stdout")) |
||||||
|
.take_while(|line| !line.is_empty()) /* non-empty lines */ |
||||||
|
.collect::<Vec<_>>(); |
||||||
|
let url_lines = url_lines.join("\n"); |
||||||
|
|
||||||
|
let urls = Regex::new(r"http://[a-zA-Z0-9\.\[\]:/]+") |
||||||
|
.unwrap() |
||||||
|
.captures_iter(url_lines.as_str()) |
||||||
|
.map(|caps| caps.get(0).unwrap().as_str()) |
||||||
|
.collect::<Vec<_>>(); |
||||||
|
|
||||||
|
assert!(!urls.is_empty()); |
||||||
|
|
||||||
|
for url in urls { |
||||||
|
reqwest::blocking::get(url)?.error_for_status()?; |
||||||
|
} |
||||||
|
|
||||||
|
child.kill()?; |
||||||
|
|
||||||
|
Ok(()) |
||||||
|
} |
@ -0,0 +1,37 @@ |
|||||||
|
mod fixtures; |
||||||
|
mod utils; |
||||||
|
|
||||||
|
use fixtures::{server, Error, TestServer}; |
||||||
|
use rstest::rstest; |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn cors(#[with(&["--cors"])] server: TestServer) -> Result<(), Error> { |
||||||
|
let resp = reqwest::blocking::get(server.url())?; |
||||||
|
|
||||||
|
assert_eq!( |
||||||
|
resp.headers().get("access-control-allow-origin").unwrap(), |
||||||
|
"*" |
||||||
|
); |
||||||
|
assert_eq!( |
||||||
|
resp.headers().get("access-control-allow-headers").unwrap(), |
||||||
|
"range, content-type, accept, origin, www-authenticate" |
||||||
|
); |
||||||
|
|
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn cors_options(#[with(&["--cors"])] server: TestServer) -> Result<(), Error> { |
||||||
|
let resp = fetch!(b"OPTIONS", server.url()).send()?; |
||||||
|
|
||||||
|
assert_eq!( |
||||||
|
resp.headers().get("access-control-allow-origin").unwrap(), |
||||||
|
"*" |
||||||
|
); |
||||||
|
assert_eq!( |
||||||
|
resp.headers().get("access-control-allow-headers").unwrap(), |
||||||
|
"range, content-type, accept, origin, www-authenticate" |
||||||
|
); |
||||||
|
|
||||||
|
Ok(()) |
||||||
|
} |
@ -0,0 +1,29 @@ |
|||||||
|
-----BEGIN CERTIFICATE----- |
||||||
|
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 |
||||||
|
-----END CERTIFICATE----- |
@ -0,0 +1,3 @@ |
|||||||
|
#!/usr/bin/env bash |
||||||
|
openssl req -subj '/CN=localhost' -x509 -newkey rsa:4096 -keyout key_pkcs8.pem -out cert.pem -nodes -days 3650 |
||||||
|
openssl rsa -in key_pkcs8.pem -out key_pkcs1.pem |
@ -0,0 +1,51 @@ |
|||||||
|
-----BEGIN RSA PRIVATE KEY----- |
||||||
|
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 |
||||||
|
-----END RSA PRIVATE KEY----- |
@ -0,0 +1,52 @@ |
|||||||
|
-----BEGIN PRIVATE KEY----- |
||||||
|
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 |
||||||
|
afHJpnY0yZEqN1PeDaKVOCQAHbD8qQ== |
||||||
|
-----END PRIVATE KEY----- |
@ -0,0 +1,25 @@ |
|||||||
|
mod fixtures; |
||||||
|
mod utils; |
||||||
|
|
||||||
|
use fixtures::{server, Error, TestServer}; |
||||||
|
use rstest::rstest; |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn default_favicon(server: TestServer) -> Result<(), Error> { |
||||||
|
let resp = reqwest::blocking::get(format!("{}favicon.ico", server.url()))?; |
||||||
|
assert_eq!(resp.status(), 200); |
||||||
|
assert_eq!(resp.headers().get("content-type").unwrap(), "image/x-icon"); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn exist_favicon(#[with(&["-A"])] server: TestServer) -> Result<(), Error> { |
||||||
|
let url = format!("{}favicon.ico", server.url()); |
||||||
|
let data = b"abc"; |
||||||
|
let resp = fetch!(b"PUT", &url).body(data.to_vec()).send()?; |
||||||
|
assert_eq!(resp.status(), 201); |
||||||
|
let resp = reqwest::blocking::get(url)?; |
||||||
|
assert_eq!(resp.status(), 200); |
||||||
|
assert_eq!(resp.bytes()?, data.to_vec()); |
||||||
|
Ok(()) |
||||||
|
} |
@ -0,0 +1,191 @@ |
|||||||
|
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}; |
||||||
|
|
||||||
|
#[allow(dead_code)] |
||||||
|
pub type Error = Box<dyn std::error::Error>; |
||||||
|
|
||||||
|
/// File names for testing purpose
|
||||||
|
#[allow(dead_code)] |
||||||
|
pub static FILES: &[&str] = &[ |
||||||
|
"test.txt", |
||||||
|
"test.html", |
||||||
|
"index.html", |
||||||
|
"test.mkv", |
||||||
|
#[cfg(not(windows))] |
||||||
|
"test \" \' & < >.csv", |
||||||
|
"😀.data", |
||||||
|
"⎙.mp4", |
||||||
|
"#[]{}()@!$&'`+,;= %20.test", |
||||||
|
#[cfg(unix)] |
||||||
|
":?#[]{}<>()@!$&'`|*+,;= %20.test", |
||||||
|
#[cfg(not(windows))] |
||||||
|
"foo\\bar.test", |
||||||
|
]; |
||||||
|
|
||||||
|
/// Directory names for testing purpose
|
||||||
|
#[allow(dead_code)] |
||||||
|
pub static DIR_NO_INDEX: &str = "dir-no-index/"; |
||||||
|
|
||||||
|
/// Directory names for testing purpose
|
||||||
|
#[allow(dead_code)] |
||||||
|
pub static DIRECTORIES: &[&str] = &["dira/", "dirb/", "dirc/", DIR_NO_INDEX]; |
||||||
|
|
||||||
|
/// Name of a deeply nested file
|
||||||
|
#[allow(dead_code)] |
||||||
|
pub static DEEPLY_NESTED_FILE: &str = "very/deeply/nested/test.rs"; |
||||||
|
|
||||||
|
/// 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"); |
||||||
|
for file in FILES { |
||||||
|
tmpdir |
||||||
|
.child(file) |
||||||
|
.write_str(&format!("This is {}", file)) |
||||||
|
.expect("Couldn't write to file"); |
||||||
|
} |
||||||
|
for directory in DIRECTORIES { |
||||||
|
for file in FILES { |
||||||
|
if *directory == DIR_NO_INDEX { |
||||||
|
continue; |
||||||
|
} |
||||||
|
tmpdir |
||||||
|
.child(format!("{}{}", directory, file)) |
||||||
|
.write_str(&format!("This is {}{}", directory, file)) |
||||||
|
.expect("Couldn't write to file"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
tmpdir |
||||||
|
.child(&DEEPLY_NESTED_FILE) |
||||||
|
.write_str("File in a deeply nested directory.") |
||||||
|
.expect("Couldn't write to file"); |
||||||
|
tmpdir |
||||||
|
} |
||||||
|
|
||||||
|
/// Get a free port.
|
||||||
|
#[fixture] |
||||||
|
#[allow(dead_code)] |
||||||
|
pub fn port() -> u16 { |
||||||
|
free_local_port().expect("Couldn't find a free local port") |
||||||
|
} |
||||||
|
|
||||||
|
/// 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, |
||||||
|
I::Item: AsRef<std::ffi::OsStr>, |
||||||
|
{ |
||||||
|
let port = port(); |
||||||
|
let tmpdir = tmpdir(); |
||||||
|
let child = Command::cargo_bin("duf") |
||||||
|
.expect("Couldn't find test binary") |
||||||
|
.env("RUST_LOG", "false") |
||||||
|
.arg(tmpdir.path()) |
||||||
|
.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("duf") |
||||||
|
.expect("Couldn't find test binary") |
||||||
|
.env("RUST_LOG", "false") |
||||||
|
.arg(tmpdir.path()) |
||||||
|
.arg("-p") |
||||||
|
.arg(port.to_string()) |
||||||
|
.args(args.clone()) |
||||||
|
.stdout(Stdio::null()) |
||||||
|
.stderr(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) |
||||||
|
} |
||||||
|
|
||||||
|
/// Wait a max of 1s for the port to become available.
|
||||||
|
fn wait_for_port(port: u16) { |
||||||
|
let start_wait = Instant::now(); |
||||||
|
|
||||||
|
while !port_check::is_port_reachable(format!("localhost:{}", port)) { |
||||||
|
sleep(Duration::from_millis(100)); |
||||||
|
|
||||||
|
if start_wait.elapsed().as_secs() > 1 { |
||||||
|
panic!("timeout waiting for port {}", port); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[allow(dead_code)] |
||||||
|
pub struct TestServer { |
||||||
|
port: u16, |
||||||
|
tmpdir: TempDir, |
||||||
|
child: Child, |
||||||
|
is_tls: bool, |
||||||
|
} |
||||||
|
|
||||||
|
#[allow(dead_code)] |
||||||
|
impl TestServer { |
||||||
|
pub fn new(port: u16, tmpdir: TempDir, child: Child, is_tls: bool) -> Self { |
||||||
|
Self { |
||||||
|
port, |
||||||
|
tmpdir, |
||||||
|
child, |
||||||
|
is_tls, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub fn url(&self) -> Url { |
||||||
|
let protocol = if self.is_tls { "https" } else { "http" }; |
||||||
|
Url::parse(&format!("{}://localhost:{}", protocol, self.port)).unwrap() |
||||||
|
} |
||||||
|
|
||||||
|
pub fn path(&self) -> &std::path::Path { |
||||||
|
self.tmpdir.path() |
||||||
|
} |
||||||
|
|
||||||
|
pub fn port(&self) -> u16 { |
||||||
|
self.port |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Drop for TestServer { |
||||||
|
fn drop(&mut self) { |
||||||
|
self.child.kill().expect("Couldn't kill test server"); |
||||||
|
self.child.wait().unwrap(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,184 @@ |
|||||||
|
mod fixtures; |
||||||
|
mod utils; |
||||||
|
|
||||||
|
use fixtures::{server, Error, TestServer}; |
||||||
|
use rstest::rstest; |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn get_dir(server: TestServer) -> Result<(), Error> { |
||||||
|
let resp = reqwest::blocking::get(server.url())?; |
||||||
|
assert_index_resp!(resp); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn head_dir(server: TestServer) -> Result<(), Error> { |
||||||
|
let resp = fetch!(b"HEAD", server.url()).send()?; |
||||||
|
assert_eq!(resp.status(), 200); |
||||||
|
assert_eq!( |
||||||
|
resp.headers().get("content-type").unwrap(), |
||||||
|
"text/html; charset=utf-8" |
||||||
|
); |
||||||
|
assert_eq!(resp.text()?, ""); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn get_dir_404(server: TestServer) -> Result<(), Error> { |
||||||
|
let resp = reqwest::blocking::get(format!("{}404/", server.url()))?; |
||||||
|
assert_eq!(resp.status(), 404); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn head_dir_404(server: TestServer) -> Result<(), Error> { |
||||||
|
let resp = fetch!(b"HEAD", format!("{}404/", server.url())).send()?; |
||||||
|
assert_eq!(resp.status(), 404); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn get_dir_zip(server: TestServer) -> Result<(), Error> { |
||||||
|
let resp = reqwest::blocking::get(format!("{}?zip", server.url()))?; |
||||||
|
assert_eq!(resp.status(), 200); |
||||||
|
assert_eq!( |
||||||
|
resp.headers().get("content-type").unwrap(), |
||||||
|
"application/zip" |
||||||
|
); |
||||||
|
assert!(resp.headers().contains_key("content-disposition")); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn head_dir_zip(server: TestServer) -> Result<(), Error> { |
||||||
|
let resp = fetch!(b"HEAD", format!("{}?zip", server.url())).send()?; |
||||||
|
assert_eq!(resp.status(), 200); |
||||||
|
assert_eq!( |
||||||
|
resp.headers().get("content-type").unwrap(), |
||||||
|
"application/zip" |
||||||
|
); |
||||||
|
assert!(resp.headers().contains_key("content-disposition")); |
||||||
|
assert_eq!(resp.text()?, ""); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn get_dir_search(server: TestServer) -> Result<(), Error> { |
||||||
|
let resp = reqwest::blocking::get(format!("{}?q={}", server.url(), "test.html"))?; |
||||||
|
assert_eq!(resp.status(), 200); |
||||||
|
let paths = utils::retrive_index_paths(&resp.text()?); |
||||||
|
assert!(!paths.is_empty()); |
||||||
|
for p in paths { |
||||||
|
assert!(p.contains(&"test.html")); |
||||||
|
} |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn head_dir_search(server: TestServer) -> Result<(), Error> { |
||||||
|
let resp = fetch!(b"HEAD", format!("{}?q={}", server.url(), "test.html")).send()?; |
||||||
|
assert_eq!(resp.status(), 200); |
||||||
|
assert_eq!( |
||||||
|
resp.headers().get("content-type").unwrap(), |
||||||
|
"text/html; charset=utf-8" |
||||||
|
); |
||||||
|
assert_eq!(resp.text()?, ""); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn get_file(server: TestServer) -> Result<(), Error> { |
||||||
|
let resp = reqwest::blocking::get(format!("{}index.html", server.url()))?; |
||||||
|
assert_eq!(resp.status(), 200); |
||||||
|
assert_eq!(resp.headers().get("content-type").unwrap(), "text/html"); |
||||||
|
assert_eq!(resp.headers().get("accept-ranges").unwrap(), "bytes"); |
||||||
|
assert!(resp.headers().contains_key("etag")); |
||||||
|
assert!(resp.headers().contains_key("last-modified")); |
||||||
|
assert!(resp.headers().contains_key("content-length")); |
||||||
|
assert_eq!(resp.text()?, "This is index.html"); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn head_file(server: TestServer) -> Result<(), Error> { |
||||||
|
let resp = fetch!(b"HEAD", format!("{}index.html", server.url())).send()?; |
||||||
|
assert_eq!(resp.status(), 200); |
||||||
|
assert_eq!(resp.headers().get("content-type").unwrap(), "text/html"); |
||||||
|
assert_eq!(resp.headers().get("accept-ranges").unwrap(), "bytes"); |
||||||
|
assert!(resp.headers().contains_key("etag")); |
||||||
|
assert!(resp.headers().contains_key("last-modified")); |
||||||
|
assert!(resp.headers().contains_key("content-length")); |
||||||
|
assert_eq!(resp.text()?, ""); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn get_file_404(server: TestServer) -> Result<(), Error> { |
||||||
|
let resp = reqwest::blocking::get(format!("{}404", server.url()))?; |
||||||
|
assert_eq!(resp.status(), 404); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn head_file_404(server: TestServer) -> Result<(), Error> { |
||||||
|
let resp = fetch!(b"HEAD", format!("{}404", server.url())).send()?; |
||||||
|
assert_eq!(resp.status(), 404); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn options_dir(server: TestServer) -> Result<(), Error> { |
||||||
|
let resp = fetch!(b"OPTIONS", format!("{}index.html", server.url())).send()?; |
||||||
|
assert_eq!(resp.status(), 204); |
||||||
|
assert_eq!( |
||||||
|
resp.headers().get("allow").unwrap(), |
||||||
|
"GET,HEAD,PUT,OPTIONS,DELETE,PROPFIND,COPY,MOVE" |
||||||
|
); |
||||||
|
assert_eq!(resp.headers().get("dav").unwrap(), "1"); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn put_file(#[with(&["-A"])] server: TestServer) -> Result<(), Error> { |
||||||
|
let url = format!("{}file1", server.url()); |
||||||
|
let resp = fetch!(b"PUT", &url).body(b"abc".to_vec()).send()?; |
||||||
|
assert_eq!(resp.status(), 201); |
||||||
|
let resp = reqwest::blocking::get(url)?; |
||||||
|
assert_eq!(resp.status(), 200); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn put_file_create_dir(#[with(&["-A"])] server: TestServer) -> Result<(), Error> { |
||||||
|
let url = format!("{}xyz/file1", server.url()); |
||||||
|
let resp = fetch!(b"PUT", &url).body(b"abc".to_vec()).send()?; |
||||||
|
assert_eq!(resp.status(), 201); |
||||||
|
let resp = reqwest::blocking::get(url)?; |
||||||
|
assert_eq!(resp.status(), 200); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn put_file_conflict_dir(#[with(&["-A"])] server: TestServer) -> Result<(), Error> { |
||||||
|
let url = format!("{}dira", server.url()); |
||||||
|
let resp = fetch!(b"PUT", &url).body(b"abc".to_vec()).send()?; |
||||||
|
assert_eq!(resp.status(), 403); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn delete_file(#[with(&["-A"])] server: TestServer) -> Result<(), Error> { |
||||||
|
let url = format!("{}test.html", server.url()); |
||||||
|
let resp = fetch!(b"DELETE", &url).send()?; |
||||||
|
assert_eq!(resp.status(), 204); |
||||||
|
let resp = reqwest::blocking::get(url)?; |
||||||
|
assert_eq!(resp.status(), 404); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn delete_file_404(#[with(&["-A"])] server: TestServer) -> Result<(), Error> { |
||||||
|
let resp = fetch!(b"DELETE", format!("{}file1", server.url())).send()?; |
||||||
|
assert_eq!(resp.status(), 404); |
||||||
|
Ok(()) |
||||||
|
} |
@ -0,0 +1,30 @@ |
|||||||
|
mod fixtures; |
||||||
|
mod utils; |
||||||
|
|
||||||
|
use fixtures::{server, Error, TestServer}; |
||||||
|
use rstest::rstest; |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn path_prefix_index(#[with(&["--path-prefix", "xyz"])] server: TestServer) -> Result<(), Error> { |
||||||
|
let resp = reqwest::blocking::get(format!("{}{}", server.url(), "xyz"))?; |
||||||
|
assert_index_resp!(resp); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn path_prefix_file(#[with(&["--path-prefix", "xyz"])] server: TestServer) -> Result<(), Error> { |
||||||
|
let resp = reqwest::blocking::get(format!("{}{}/index.html", server.url(), "xyz"))?; |
||||||
|
assert_eq!(resp.status(), 200); |
||||||
|
assert_eq!(resp.text()?, "This is index.html"); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn path_prefix_propfind( |
||||||
|
#[with(&["--path-prefix", "xyz"])] server: TestServer, |
||||||
|
) -> Result<(), Error> { |
||||||
|
let resp = fetch!(b"PROPFIND", format!("{}{}", server.url(), "xyz")).send()?; |
||||||
|
let text = resp.text()?; |
||||||
|
assert!(text.contains("<D:href>/xyz/</D:href>")); |
||||||
|
Ok(()) |
||||||
|
} |
@ -0,0 +1,35 @@ |
|||||||
|
mod fixtures; |
||||||
|
|
||||||
|
use fixtures::{server, Error, TestServer, DIR_NO_INDEX}; |
||||||
|
use rstest::rstest; |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn render_index(#[with(&["--render-index"])] server: TestServer) -> Result<(), Error> { |
||||||
|
let resp = reqwest::blocking::get(server.url())?; |
||||||
|
let text = resp.text()?; |
||||||
|
assert_eq!(text, "This is index.html"); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn render_index_404(#[with(&["--render-index"])] server: TestServer) -> Result<(), Error> { |
||||||
|
let resp = reqwest::blocking::get(format!("{}/{}", server.url(), DIR_NO_INDEX))?; |
||||||
|
assert_eq!(resp.status(), 404); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn render_spa(#[with(&["--render-spa"])] server: TestServer) -> Result<(), Error> { |
||||||
|
let resp = reqwest::blocking::get(server.url())?; |
||||||
|
let text = resp.text()?; |
||||||
|
assert_eq!(text, "This is index.html"); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn render_spa_no_404(#[with(&["--render-spa"])] server: TestServer) -> Result<(), Error> { |
||||||
|
let resp = reqwest::blocking::get(format!("{}/{}", server.url(), DIR_NO_INDEX))?; |
||||||
|
let text = resp.text()?; |
||||||
|
assert_eq!(text, "This is index.html"); |
||||||
|
Ok(()) |
||||||
|
} |
@ -0,0 +1,46 @@ |
|||||||
|
mod fixtures; |
||||||
|
mod utils; |
||||||
|
|
||||||
|
use assert_fs::fixture::TempDir; |
||||||
|
use fixtures::{server, tmpdir, Error, TestServer}; |
||||||
|
use rstest::rstest; |
||||||
|
|
||||||
|
#[cfg(unix)] |
||||||
|
use std::os::unix::fs::symlink as symlink_dir; |
||||||
|
#[cfg(windows)] |
||||||
|
use std::os::windows::fs::symlink_dir; |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn default_not_allow_symlink(server: TestServer, tmpdir: TempDir) -> Result<(), Error> { |
||||||
|
// Create symlink directory "foo" to point outside the root
|
||||||
|
let dir = "foo"; |
||||||
|
symlink_dir(tmpdir.path(), server.path().join(dir)).expect("Couldn't create symlink"); |
||||||
|
let resp = reqwest::blocking::get(format!("{}{}", server.url(), dir))?; |
||||||
|
assert_eq!(resp.status(), 404); |
||||||
|
let resp = reqwest::blocking::get(format!("{}{}/index.html", server.url(), dir))?; |
||||||
|
assert_eq!(resp.status(), 404); |
||||||
|
let resp = reqwest::blocking::get(server.url())?; |
||||||
|
let paths = utils::retrive_index_paths(&resp.text()?); |
||||||
|
assert!(!paths.is_empty()); |
||||||
|
assert!(!paths.contains(&format!("{}/", dir))); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn allow_symlink( |
||||||
|
#[with(&["--allow-symlink"])] server: TestServer, |
||||||
|
tmpdir: TempDir, |
||||||
|
) -> Result<(), Error> { |
||||||
|
// Create symlink directory "foo" to point outside the root
|
||||||
|
let dir = "foo"; |
||||||
|
symlink_dir(tmpdir.path(), server.path().join(dir)).expect("Couldn't create symlink"); |
||||||
|
let resp = reqwest::blocking::get(format!("{}{}", server.url(), dir))?; |
||||||
|
assert_eq!(resp.status(), 200); |
||||||
|
let resp = reqwest::blocking::get(format!("{}{}/index.html", server.url(), dir))?; |
||||||
|
assert_eq!(resp.status(), 200); |
||||||
|
let resp = reqwest::blocking::get(server.url())?; |
||||||
|
let paths = utils::retrive_index_paths(&resp.text()?); |
||||||
|
assert!(!paths.is_empty()); |
||||||
|
assert!(paths.contains(&format!("{}/", dir))); |
||||||
|
Ok(()) |
||||||
|
} |
@ -0,0 +1,51 @@ |
|||||||
|
mod fixtures; |
||||||
|
mod utils; |
||||||
|
|
||||||
|
use assert_cmd::Command; |
||||||
|
use fixtures::{server, Error, TestServer}; |
||||||
|
use predicates::str::contains; |
||||||
|
use reqwest::blocking::ClientBuilder; |
||||||
|
use rstest::rstest; |
||||||
|
|
||||||
|
/// Can start the server with TLS and receive encrypted responses.
|
||||||
|
#[rstest] |
||||||
|
#[case(server(&[ |
||||||
|
"--tls-cert", "tests/data/cert.pem", |
||||||
|
"--tls-key", "tests/data/key_pkcs8.pem", |
||||||
|
]))] |
||||||
|
#[case(server(&[ |
||||||
|
"--tls-cert", "tests/data/cert.pem", |
||||||
|
"--tls-key", "tests/data/key_pkcs1.pem", |
||||||
|
]))] |
||||||
|
fn tls_works(#[case] server: TestServer) -> Result<(), Error> { |
||||||
|
let client = ClientBuilder::new() |
||||||
|
.danger_accept_invalid_certs(true) |
||||||
|
.build()?; |
||||||
|
let resp = client.get(server.url()).send()?.error_for_status()?; |
||||||
|
assert_index_resp!(resp); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
/// Wrong path for cert throws error.
|
||||||
|
#[rstest] |
||||||
|
fn wrong_path_cert() -> Result<(), Error> { |
||||||
|
Command::cargo_bin("duf")? |
||||||
|
.args(&["--tls-cert", "wrong", "--tls-key", "tests/data/key.pem"]) |
||||||
|
.assert() |
||||||
|
.failure() |
||||||
|
.stderr(contains("error: Failed to access `wrong`")); |
||||||
|
|
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
/// Wrong paths for key throws errors.
|
||||||
|
#[rstest] |
||||||
|
fn wrong_path_key() -> Result<(), Error> { |
||||||
|
Command::cargo_bin("duf")? |
||||||
|
.args(&["--tls-cert", "tests/data/cert.pem", "--tls-key", "wrong"]) |
||||||
|
.assert() |
||||||
|
.failure() |
||||||
|
.stderr(contains("error: Failed to access `wrong`")); |
||||||
|
|
||||||
|
Ok(()) |
||||||
|
} |
@ -0,0 +1,61 @@ |
|||||||
|
use serde_json::Value; |
||||||
|
use std::collections::HashSet; |
||||||
|
|
||||||
|
#[macro_export] |
||||||
|
macro_rules! assert_index_resp { |
||||||
|
($resp:ident) => { |
||||||
|
assert_index_resp!($resp, self::fixtures::FILES) |
||||||
|
}; |
||||||
|
($resp:ident, $files:expr) => { |
||||||
|
assert_eq!($resp.status(), 200); |
||||||
|
let body = $resp.text()?; |
||||||
|
let paths = self::utils::retrive_index_paths(&body); |
||||||
|
assert!(!paths.is_empty()); |
||||||
|
for file in $files { |
||||||
|
assert!(paths.contains(&file.to_string())); |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
#[macro_export] |
||||||
|
macro_rules! fetch { |
||||||
|
($method:literal, $url:expr) => { |
||||||
|
reqwest::blocking::Client::new().request(hyper::Method::from_bytes($method)?, $url) |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
#[allow(dead_code)] |
||||||
|
pub fn retrive_index_paths(index: &str) -> HashSet<String> { |
||||||
|
retrive_index_paths_impl(index).unwrap_or_default() |
||||||
|
} |
||||||
|
|
||||||
|
#[allow(dead_code)] |
||||||
|
pub fn encode_uri(v: &str) -> String { |
||||||
|
let parts: Vec<_> = v.split('/').map(urlencoding::encode).collect(); |
||||||
|
parts.join("/") |
||||||
|
} |
||||||
|
|
||||||
|
fn retrive_index_paths_impl(index: &str) -> Option<HashSet<String>> { |
||||||
|
let lines: Vec<&str> = index.lines().collect(); |
||||||
|
let (i, _) = lines |
||||||
|
.iter() |
||||||
|
.enumerate() |
||||||
|
.find(|(_, v)| v.contains("const DATA"))?; |
||||||
|
let line = lines.get(i + 1)?; |
||||||
|
let value: Value = line.parse().ok()?; |
||||||
|
let paths = value |
||||||
|
.get("paths")? |
||||||
|
.as_array()? |
||||||
|
.iter() |
||||||
|
.flat_map(|v| { |
||||||
|
let name = v.get("name")?.as_str()?; |
||||||
|
let path_type = v.get("path_type")?.as_str()?; |
||||||
|
if path_type.ends_with("Dir") { |
||||||
|
Some(format!("{}/", name)) |
||||||
|
} else { |
||||||
|
Some(name.to_owned()) |
||||||
|
} |
||||||
|
}) |
||||||
|
.collect(); |
||||||
|
Some(paths) |
||||||
|
} |
@ -0,0 +1,203 @@ |
|||||||
|
mod fixtures; |
||||||
|
mod utils; |
||||||
|
|
||||||
|
use fixtures::{server, Error, TestServer, FILES}; |
||||||
|
use rstest::rstest; |
||||||
|
use xml::escape::escape_str_pcdata; |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn propfind_dir(server: TestServer) -> Result<(), Error> { |
||||||
|
let resp = fetch!(b"PROPFIND", format!("{}dira", server.url())).send()?; |
||||||
|
assert_eq!(resp.status(), 207); |
||||||
|
let body = resp.text()?; |
||||||
|
assert!(body.contains("<D:href>/dira</D:href>")); |
||||||
|
assert!(body.contains("<D:displayname>dira</D:displayname>")); |
||||||
|
for f in FILES { |
||||||
|
assert!(body.contains(&format!("<D:href>/dira/{}</D:href>", utils::encode_uri(f)))); |
||||||
|
assert!(body.contains(&format!( |
||||||
|
"<D:displayname>{}</D:displayname>", |
||||||
|
escape_str_pcdata(f) |
||||||
|
))); |
||||||
|
} |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn propfind_dir_depth0(server: TestServer) -> Result<(), Error> { |
||||||
|
let resp = fetch!(b"PROPFIND", format!("{}dira", server.url())) |
||||||
|
.header("depth", "0") |
||||||
|
.send()?; |
||||||
|
assert_eq!(resp.status(), 207); |
||||||
|
let body = resp.text()?; |
||||||
|
assert!(body.contains("<D:href>/dira</D:href>")); |
||||||
|
assert!(body.contains("<D:displayname>dira</D:displayname>")); |
||||||
|
assert_eq!( |
||||||
|
body.lines() |
||||||
|
.filter(|v| *v == "<D:status>HTTP/1.1 200 OK</D:status>") |
||||||
|
.count(), |
||||||
|
1 |
||||||
|
); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn propfind_404(server: TestServer) -> Result<(), Error> { |
||||||
|
let resp = fetch!(b"PROPFIND", format!("{}404", server.url())).send()?; |
||||||
|
assert_eq!(resp.status(), 404); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn propfind_file(server: TestServer) -> Result<(), Error> { |
||||||
|
let resp = fetch!(b"PROPFIND", format!("{}test.html", server.url())).send()?; |
||||||
|
assert_eq!(resp.status(), 207); |
||||||
|
let body = resp.text()?; |
||||||
|
assert!(body.contains("<D:href>/test.html</D:href>")); |
||||||
|
assert!(body.contains("<D:displayname>test.html</D:displayname>")); |
||||||
|
assert_eq!( |
||||||
|
body.lines() |
||||||
|
.filter(|v| *v == "<D:status>HTTP/1.1 200 OK</D:status>") |
||||||
|
.count(), |
||||||
|
1 |
||||||
|
); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn proppatch_file(#[with(&["-A"])] server: TestServer) -> Result<(), Error> { |
||||||
|
let resp = fetch!(b"PROPPATCH", format!("{}test.html", server.url())).send()?; |
||||||
|
assert_eq!(resp.status(), 207); |
||||||
|
let body = resp.text()?; |
||||||
|
assert!(body.contains("<D:href>/test.html</D:href>")); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn proppatch_404(#[with(&["-A"])] server: TestServer) -> Result<(), Error> { |
||||||
|
let resp = fetch!(b"PROPPATCH", format!("{}404", server.url())).send()?; |
||||||
|
assert_eq!(resp.status(), 404); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn mkcol_dir(#[with(&["-A"])] server: TestServer) -> Result<(), Error> { |
||||||
|
let resp = fetch!(b"MKCOL", format!("{}newdir", server.url())).send()?; |
||||||
|
assert_eq!(resp.status(), 201); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn mkcol_not_allow_upload(server: TestServer) -> Result<(), Error> { |
||||||
|
let resp = fetch!(b"MKCOL", format!("{}newdir", server.url())).send()?; |
||||||
|
assert_eq!(resp.status(), 403); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn copy_file(#[with(&["-A"])] server: TestServer) -> Result<(), Error> { |
||||||
|
let new_url = format!("{}test2.html", server.url()); |
||||||
|
let resp = fetch!(b"COPY", format!("{}test.html", server.url())) |
||||||
|
.header("Destination", &new_url) |
||||||
|
.send()?; |
||||||
|
assert_eq!(resp.status(), 204); |
||||||
|
let resp = reqwest::blocking::get(new_url)?; |
||||||
|
assert_eq!(resp.status(), 200); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn copy_not_allow_upload(server: TestServer) -> Result<(), Error> { |
||||||
|
let new_url = format!("{}test2.html", server.url()); |
||||||
|
let resp = fetch!(b"COPY", format!("{}test.html", server.url())) |
||||||
|
.header("Destination", &new_url) |
||||||
|
.send()?; |
||||||
|
assert_eq!(resp.status(), 403); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn copy_file_404(#[with(&["-A"])] server: TestServer) -> Result<(), Error> { |
||||||
|
let new_url = format!("{}test2.html", server.url()); |
||||||
|
let resp = fetch!(b"COPY", format!("{}404", server.url())) |
||||||
|
.header("Destination", &new_url) |
||||||
|
.send()?; |
||||||
|
assert_eq!(resp.status(), 404); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn move_file(#[with(&["-A"])] server: TestServer) -> Result<(), Error> { |
||||||
|
let origin_url = format!("{}test.html", server.url()); |
||||||
|
let new_url = format!("{}test2.html", server.url()); |
||||||
|
let resp = fetch!(b"MOVE", &origin_url) |
||||||
|
.header("Destination", &new_url) |
||||||
|
.send()?; |
||||||
|
assert_eq!(resp.status(), 204); |
||||||
|
let resp = reqwest::blocking::get(new_url)?; |
||||||
|
assert_eq!(resp.status(), 200); |
||||||
|
let resp = reqwest::blocking::get(origin_url)?; |
||||||
|
assert_eq!(resp.status(), 404); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn move_not_allow_upload(#[with(&["--allow-delete"])] server: TestServer) -> Result<(), Error> { |
||||||
|
let origin_url = format!("{}test.html", server.url()); |
||||||
|
let new_url = format!("{}test2.html", server.url()); |
||||||
|
let resp = fetch!(b"MOVE", &origin_url) |
||||||
|
.header("Destination", &new_url) |
||||||
|
.send()?; |
||||||
|
assert_eq!(resp.status(), 403); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn move_not_allow_delete(#[with(&["--allow-upload"])] server: TestServer) -> Result<(), Error> { |
||||||
|
let origin_url = format!("{}test.html", server.url()); |
||||||
|
let new_url = format!("{}test2.html", server.url()); |
||||||
|
let resp = fetch!(b"MOVE", &origin_url) |
||||||
|
.header("Destination", &new_url) |
||||||
|
.send()?; |
||||||
|
assert_eq!(resp.status(), 403); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn move_file_404(#[with(&["-A"])] server: TestServer) -> Result<(), Error> { |
||||||
|
let new_url = format!("{}test2.html", server.url()); |
||||||
|
let resp = fetch!(b"MOVE", format!("{}404", server.url())) |
||||||
|
.header("Destination", &new_url) |
||||||
|
.send()?; |
||||||
|
assert_eq!(resp.status(), 404); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn lock_file(#[with(&["-A"])] server: TestServer) -> Result<(), Error> { |
||||||
|
let resp = fetch!(b"LOCK", format!("{}test.html", server.url())).send()?; |
||||||
|
assert_eq!(resp.status(), 200); |
||||||
|
let body = resp.text()?; |
||||||
|
assert!(body.contains("<D:href>/test.html</D:href>")); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn lock_file_404(#[with(&["-A"])] server: TestServer) -> Result<(), Error> { |
||||||
|
let resp = fetch!(b"LOCK", format!("{}404", server.url())).send()?; |
||||||
|
assert_eq!(resp.status(), 404); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn unlock_file(#[with(&["-A"])] server: TestServer) -> Result<(), Error> { |
||||||
|
let resp = fetch!(b"LOCK", format!("{}test.html", server.url())).send()?; |
||||||
|
assert_eq!(resp.status(), 200); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[rstest] |
||||||
|
fn unlock_file_404(#[with(&["-A"])] server: TestServer) -> Result<(), Error> { |
||||||
|
let resp = fetch!(b"LOCK", format!("{}404", server.url())).send()?; |
||||||
|
assert_eq!(resp.status(), 404); |
||||||
|
Ok(()) |
||||||
|
} |
Loading…
Reference in new issue