diff --git a/wezterm-ssh/tests/e2e/sftp.rs b/wezterm-ssh/tests/e2e/sftp.rs index 59df2826b..b6c274536 100644 --- a/wezterm-ssh/tests/e2e/sftp.rs +++ b/wezterm-ssh/tests/e2e/sftp.rs @@ -1,5 +1,6 @@ use crate::sshd::session; use assert_fs::{prelude::*, TempDir}; +use predicates::prelude::*; use rstest::*; use ssh2::FileType; use std::path::PathBuf; @@ -21,7 +22,7 @@ fn file_type_to_str(file_type: FileType) -> &'static str { #[rstest] #[smol_potat::test] -async fn should_support_listing_directory_contents(#[future] session: Session) { +async fn readdir_should_return_list_of_directories_files_and_symlinks(#[future] session: Session) { let session = session.await; // $TEMP/dir1/ @@ -66,3 +67,105 @@ async fn should_support_listing_directory_contents(#[future] session: Session) { ] ); } + +#[rstest] +#[smol_potat::test] +async fn mkdir_should_create_a_directory_on_the_remote_filesystem(#[future] session: Session) { + let session = session.await; + + let temp = TempDir::new().unwrap(); + + session + .sftp() + .mkdir(temp.child("dir").path().to_path_buf(), 0o644) + .await + .expect("Failed to create directory"); + + // Verify the path exists and is to a directory + temp.child("dir").assert(predicate::path::is_dir()); +} + +#[rstest] +#[smol_potat::test] +async fn mkdir_should_return_error_if_unable_to_create_directory(#[future] session: Session) { + let session = session.await; + + let temp = TempDir::new().unwrap(); + + // Attempt to create a nested directory structure, which is not supported + let result = session + .sftp() + .mkdir(temp.child("dir").child("dir").path().to_path_buf(), 0o644) + .await; + assert!( + result.is_err(), + "Unexpectedly succeeded in creating directory" + ); + + // Verify the path is not a directory + temp.child("dir") + .child("dir") + .assert(predicate::path::is_dir().not()); + temp.child("dir").assert(predicate::path::is_dir().not()); +} + +#[rstest] +#[smol_potat::test] +async fn rmdir_should_remove_a_remote_directory(#[future] session: Session) { + let session = session.await; + + let temp = TempDir::new().unwrap(); + + // Removing an empty directory should succeed + let dir = temp.child("dir"); + dir.create_dir_all().unwrap(); + session + .sftp() + .rmdir(dir.path().to_path_buf()) + .await + .expect("Failed to remove directory"); + + // Verify the directory no longer exists + dir.assert(predicate::path::is_dir().not()); +} + +#[rstest] +#[smol_potat::test] +async fn rmdir_should_return_an_error_if_failed_to_remove_directory(#[future] session: Session) { + let session = session.await; + + let temp = TempDir::new().unwrap(); + + // Attempt to remove a missing path + let result = session + .sftp() + .rmdir(temp.child("missing-dir").path().to_path_buf()) + .await; + assert!( + result.is_err(), + "Unexpectedly succeeded in removing missing directory" + ); + + // Attempt to remove a non-empty directory + let dir = temp.child("dir"); + dir.create_dir_all().unwrap(); + dir.child("file").touch().unwrap(); + + let result = session.sftp().rmdir(dir.path().to_path_buf()).await; + assert!( + result.is_err(), + "Unexpectedly succeeded in removing non-empty directory" + ); + + // Verify the non-empty directory still exists + dir.assert(predicate::path::is_dir()); + + // Attempt to remove a file (not a directory) + let file = temp.child("file"); + file.touch().unwrap(); + let result = session.sftp().rmdir(file.path().to_path_buf()).await; + assert!(result.is_err(), "Unexpectedly succeeded in removing file"); + + // Verify the file still exists + file.assert(predicate::path::is_file()); +} diff --git a/wezterm-ssh/tests/sshd.rs b/wezterm-ssh/tests/sshd.rs index 94504c3a0..85b9a96b3 100644 --- a/wezterm-ssh/tests/sshd.rs +++ b/wezterm-ssh/tests/sshd.rs @@ -368,8 +368,8 @@ impl Sshd { .arg(log_path.as_ref()) .spawn()?; - // Pause for couple of seconds to make sure that the server didn't die due to an error - thread::sleep(Duration::from_secs(2)); + // Pause to make sure that the server didn't die due to an error + thread::sleep(Duration::from_millis(100)); if let Some(exit_status) = child.try_wait()? { let output = child.wait_with_output()?; @@ -391,6 +391,9 @@ impl Drop for Sshd { /// Kills server upon drop fn drop(&mut self) { let _ = self.child.kill(); + + // NOTE: Should wait to ensure that the process does not become a zombie + let _ = self.child.wait(); } }