Use containers to test file transfers

Use containers to test file transfers

Container setup

Container setup

tests with docker-compose

these tests won't work with containers

ftp tests with containers; removed crap servers; tests only lib

hostname for github

booooooh

fixed recursive remove FTP

Use containers to test file transfers

Container setup

Container setup

tests with docker-compose

these tests won't work with containers

ftp tests with containers; removed crap servers; tests only lib

hostname for github

booooooh

fixed recursive remove FTP

fixed rename

changelog

Desperate attempt

Fixed ftp tests; migrated sftp tests to containers; use env for services

github actions are just broken imo

github actions are just broken imo

Don't use services, since they just don't fuckin work...

docker compose not supported yet?

deprecated typo

Now explain this: github actions have services (which don't work) and then you find out docker-compose is already installed *BLOWMIND*

Fixed hostname for tests

scp tests

maybe

tests

wtf

Restored host tests

Changelog

Improving coverage

Improving coverage

Restored coverage task

More tests for file transfers; test ssh keys too

typo

tests; code improvements

Use tests helpers

fixed tempdir

fixed tempdir
This commit is contained in:
veeso 2021-06-12 16:17:47 +02:00
parent 6cdd56e22f
commit 1c58f1d623
25 changed files with 1393 additions and 1050 deletions

View File

@ -1,4 +1,4 @@
name: coverage name: Coverage
on: [push, pull_request] on: [push, pull_request]
@ -6,22 +6,23 @@ env:
CARGO_TERM_COLOR: always CARGO_TERM_COLOR: always
jobs: jobs:
coverage: build:
name: Generate coverage
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout repository - uses: actions/checkout@v2
uses: actions/checkout@v2 - name: Setup containers
- name: Setup rust toolchain run: docker-compose -f "tests/docker-compose.yml" up -d --build
- name: Setup nightly toolchain
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
with: with:
toolchain: nightly toolchain: nightly
override: true override: true
- name: Run tests - name: Run tests (nightly)
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: test command: test
args: --all-features --no-fail-fast args: --all-features --lib --no-fail-fast
env: env:
CARGO_INCREMENTAL: "0" CARGO_INCREMENTAL: "0"
RUSTFLAGS: "-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests" RUSTFLAGS: "-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests"

View File

@ -11,15 +11,18 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Setup containers
run: docker-compose -f "tests/docker-compose.yml" up -d --build
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
with: with:
toolchain: stable toolchain: stable
override: true override: true
components: rustfmt, clippy components: rustfmt, clippy
- uses: actions-rs/cargo@v1 - name: Run tests
uses: actions-rs/cargo@v1
with: with:
command: test command: test
args: --all-features --no-fail-fast args: --all-features --lib --no-fail-fast
- name: Format - name: Format
run: cargo fmt --all -- --check run: cargo fmt --all -- --check
- name: Clippy - name: Clippy

View File

@ -14,6 +14,6 @@ jobs:
- name: Build - name: Build
run: cargo build --verbose run: cargo build --verbose
- name: Run tests - name: Run tests
run: cargo test --verbose --features githubActions -- --test-threads 1 run: cargo test --verbose --lib --features github-actions -- --test-threads 1
- name: Clippy - name: Clippy
run: cargo clippy run: cargo clippy

View File

@ -14,6 +14,6 @@ jobs:
- name: Build - name: Build
run: cargo build --verbose run: cargo build --verbose
- name: Run tests - name: Run tests
run: cargo test --verbose --features githubActions -- --test-threads 1 run: cargo test --verbose --lib --features github-actions -- --test-threads 1
- name: Clippy - name: Clippy
run: cargo clippy run: cargo clippy

View File

@ -22,6 +22,12 @@
Released on FIXME: ?? Released on FIXME: ??
- Enhancements:
- **CI now uses containers to test file transfers (SSH/FTP)**
- Improved coverage
- Found many bugs which has now been fixed
- Build in CI won't fail due to test servers not responding
- We're now able to test all the functionalities of the file transfers
- Bugfix: - Bugfix:
- Fixed broken input cursor when typing UTF8 characters (tui-realm 0.3.2) - Fixed broken input cursor when typing UTF8 characters (tui-realm 0.3.2)
- Fixed [Issue 44](https://github.com/veeso/termscp/issues/44): Could not move files to other paths in FTP - Fixed [Issue 44](https://github.com/veeso/termscp/issues/44): Could not move files to other paths in FTP

View File

@ -59,7 +59,8 @@ wildmatch = "2.0.0"
pretty_assertions = "0.7.2" pretty_assertions = "0.7.2"
[features] [features]
githubActions = [] github-actions = []
with-containers = []
[target."cfg(any(target_os = \"unix\", target_os = \"macos\", target_os = \"linux\"))"] [target."cfg(any(target_os = \"unix\", target_os = \"macos\", target_os = \"linux\"))"]
[target."cfg(any(target_os = \"unix\", target_os = \"macos\", target_os = \"linux\"))".dependencies] [target."cfg(any(target_os = \"unix\", target_os = \"macos\", target_os = \"linux\"))".dependencies]

View File

@ -3,15 +3,26 @@
Document audience: developers Document audience: developers
- [Developer Manual](#developer-manual) - [Developer Manual](#developer-manual)
- [How to test](#how-to-test)
- [How termscp works](#how-termscp-works) - [How termscp works](#how-termscp-works)
- [Activities](#activities) - [Activities](#activities)
- [The Context](#the-context) - [The Context](#the-context)
- [Tests fails due to receivers](#tests-fails-due-to-receivers)
- [Implementing File Transfers](#implementing-file-transfers) - [Implementing File Transfers](#implementing-file-transfers)
Welcome to the developer manual for termscp. This chapter DOESN'T contain the documentation for termscp modules, which can instead be found on Rust Docs at <https://docs.rs/termscp> Welcome to the developer manual for termscp. This chapter DOESN'T contain the documentation for termscp modules, which can instead be found on Rust Docs at <https://docs.rs/termscp>
This chapter describes how termscp works and the guide lines to implement stuff such as file transfers and add features to the user interface. This chapter describes how termscp works and the guide lines to implement stuff such as file transfers and add features to the user interface.
## How to test
First an introduction to tests.
Usually it's enough to run `cargo test`, but please note that whenever you're working on file transfer you'll need one more step.
In order to run tests with file transfers, you need to start the file transfer server containers, which can be started via `docker`.
To run all tests with file transfers just run: `./tests/test.sh`
---
## How termscp works ## How termscp works
termscp is basically made up of 4 components: termscp is basically made up of 4 components:
@ -62,12 +73,6 @@ The context basically holds the following data:
--- ---
## Tests fails due to receivers
Yes. This happens quite often and is related to the fact that I'm using public SSH/SFTP/FTP server to test file receivers and sometimes this server go down for even a day or more. If your tests don't pass due to this, don't worry, submit the pull request and I'll take care of testing them by myself.
---
## Implementing File Transfers ## Implementing File Transfers
This chapter describes how to implement a file transfer in termscp. A file transfer is a module which implements the `FileTransfer` trait. The file transfer provides different modules to interact with a remote server, which in addition to the most obvious methods, used to download and upload files, provides also methods to list files, delete files, create directories etc. This chapter describes how to implement a file transfer in termscp. A file transfer is a module which implements the `FileTransfer` trait. The file transfer provides different modules to interact with a remote server, which in addition to the most obvious methods, used to download and upload files, provides also methods to list files, delete files, create directories etc.

View File

@ -183,6 +183,12 @@ mod tests {
file_fmt: Some(String::from("{NAME}")), file_fmt: Some(String::from("{NAME}")),
remote_file_fmt: Some(String::from("{USER}")), remote_file_fmt: Some(String::from("{USER}")),
}; };
assert_eq!(ui.default_protocol, String::from("SFTP"));
assert_eq!(ui.text_editor, PathBuf::from("nano"));
assert_eq!(ui.show_hidden_files, true);
assert_eq!(ui.check_for_updates, Some(true));
assert_eq!(ui.group_dirs, Some(String::from("first")));
assert_eq!(ui.file_fmt, Some(String::from("{NAME}")));
let cfg: UserConfig = UserConfig { let cfg: UserConfig = UserConfig {
user_interface: ui, user_interface: ui,
remote: remote, remote: remote,

View File

@ -208,6 +208,25 @@ mod tests {
assert!(serializer.deserialize(Box::new(toml_file)).is_ok()); assert!(serializer.deserialize(Box::new(toml_file)).is_ok());
} }
#[test]
fn test_config_serializer_fail_write() {
let toml_file: tempfile::NamedTempFile = tempfile::NamedTempFile::new().ok().unwrap();
let writer: Box<dyn Write> = Box::new(std::fs::File::open(toml_file.path()).unwrap());
// Try to write unexisting file
let serializer: ConfigSerializer = ConfigSerializer {};
let cfg: UserConfig = UserConfig::default();
assert!(serializer.serialize(writer, &cfg).is_err());
}
#[test]
fn test_config_serializer_fail_read() {
let toml_file: tempfile::NamedTempFile = tempfile::NamedTempFile::new().ok().unwrap();
let reader: Box<dyn Read> = Box::new(std::fs::File::open(toml_file.path()).unwrap());
// Try to write unexisting file
let serializer: ConfigSerializer = ConfigSerializer {};
assert!(serializer.deserialize(reader).is_err());
}
fn create_good_toml() -> tempfile::NamedTempFile { fn create_good_toml() -> tempfile::NamedTempFile {
// Write // Write
let mut tmpfile: tempfile::NamedTempFile = tempfile::NamedTempFile::new().unwrap(); let mut tmpfile: tempfile::NamedTempFile = tempfile::NamedTempFile::new().unwrap();

View File

@ -491,7 +491,10 @@ impl FileTransfer for FtpFileTransfer {
info!("Disconnecting from FTP server..."); info!("Disconnecting from FTP server...");
match &mut self.stream { match &mut self.stream {
Some(stream) => match stream.quit() { Some(stream) => match stream.quit() {
Ok(_) => Ok(()), Ok(_) => {
self.stream = None;
Ok(())
}
Err(err) => Err(FileTransferError::new_ex( Err(err) => Err(FileTransferError::new_ex(
FileTransferErrorType::ConnectionError, FileTransferErrorType::ConnectionError,
err.to_string(), err.to_string(),
@ -859,9 +862,14 @@ impl FileTransfer for FtpFileTransfer {
mod tests { mod tests {
use super::*; use super::*;
use crate::utils::file::open_file;
use crate::utils::fmt::fmt_time; use crate::utils::fmt::fmt_time;
#[cfg(feature = "with-containers")]
use crate::utils::test_helpers::write_file;
use crate::utils::test_helpers::{create_sample_file_entry, make_fsentry};
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
use std::io::{Read, Write};
use std::time::Duration; use std::time::Duration;
#[test] #[test]
@ -875,120 +883,281 @@ mod tests {
assert!(ftp.stream.is_none()); assert!(ftp.stream.is_none());
} }
#[test]
#[cfg(feature = "with-containers")]
fn test_filetransfer_ftp_server() {
let mut ftp: FtpFileTransfer = FtpFileTransfer::new(false);
// Sample file
let (entry, file): (FsFile, tempfile::NamedTempFile) = create_sample_file_entry();
// Connect
#[cfg(not(feature = "github-actions"))]
let hostname: String = String::from("127.0.0.1");
#[cfg(feature = "github-actions")]
let hostname: String = String::from("127.0.0.1");
assert!(ftp
.connect(
hostname,
10021,
Some(String::from("test")),
Some(String::from("test")),
)
.is_ok());
assert_eq!(ftp.is_connected(), true);
// Get pwd
assert_eq!(ftp.pwd().unwrap(), PathBuf::from("/"));
// List dir (dir is empty)
assert_eq!(ftp.list_dir(&Path::new("/")).unwrap().len(), 0);
// Make directory
assert!(ftp.mkdir(PathBuf::from("/home").as_path()).is_ok());
// Make directory (err)
assert!(ftp.mkdir(PathBuf::from("/root/pommlar").as_path()).is_err());
// Change directory
assert!(ftp.change_dir(PathBuf::from("/home").as_path()).is_ok());
// Change directory (err)
assert!(ftp
.change_dir(PathBuf::from("/tmp/oooo/aaaa/eee").as_path())
.is_err());
// Copy (not supported)
assert!(ftp
.copy(&FsEntry::File(entry.clone()), PathBuf::from("/").as_path())
.is_err());
// Exec (not supported)
assert!(ftp.exec("echo 1;").is_err());
// Upload 2 files
let mut writable = ftp
.send_file(&entry, PathBuf::from("omar.txt").as_path())
.ok()
.unwrap();
write_file(&file, &mut writable);
assert!(ftp.on_sent(writable).is_ok());
let mut writable = ftp
.send_file(&entry, PathBuf::from("README.md").as_path())
.ok()
.unwrap();
write_file(&file, &mut writable);
assert!(ftp.on_sent(writable).is_ok());
// Upload file (err)
assert!(ftp
.send_file(&entry, PathBuf::from("/ommlar/omarone").as_path())
.is_err());
// List dir
let list: Vec<FsEntry> = ftp.list_dir(PathBuf::from("/home").as_path()).ok().unwrap();
assert_eq!(list.len(), 2);
// Find
assert!(ftp.change_dir(PathBuf::from("/").as_path()).is_ok());
assert_eq!(ftp.find("*.txt").ok().unwrap().len(), 1);
assert_eq!(ftp.find("*.md").ok().unwrap().len(), 1);
assert_eq!(ftp.find("*.jpeg").ok().unwrap().len(), 0);
assert!(ftp.change_dir(PathBuf::from("/home").as_path()).is_ok());
// Rename
assert!(ftp.mkdir(PathBuf::from("/uploads").as_path()).is_ok());
assert!(ftp
.rename(
list.get(0).unwrap(),
PathBuf::from("/uploads/README.txt").as_path()
)
.is_ok());
// Rename (err)
assert!(ftp
.rename(list.get(0).unwrap(), PathBuf::from("OMARONE").as_path())
.is_err());
let dummy: FsEntry = FsEntry::File(FsFile {
name: String::from("cucumber.txt"),
abs_path: PathBuf::from("/cucumber.txt"),
last_change_time: SystemTime::UNIX_EPOCH,
last_access_time: SystemTime::UNIX_EPOCH,
creation_time: SystemTime::UNIX_EPOCH,
size: 0,
ftype: Some(String::from("txt")), // File type
readonly: true,
symlink: None, // UNIX only
user: Some(0), // UNIX only
group: Some(0), // UNIX only
unix_pex: Some((6, 4, 4)), // UNIX only
});
assert!(ftp
.rename(&dummy, PathBuf::from("/a/b/c").as_path())
.is_err());
// Remove
assert!(ftp.remove(list.get(1).unwrap()).is_ok());
assert!(ftp.remove(list.get(1).unwrap()).is_err());
// Receive file
let mut writable = ftp
.send_file(&entry, PathBuf::from("/uploads/README.txt").as_path())
.ok()
.unwrap();
write_file(&file, &mut writable);
assert!(ftp.on_sent(writable).is_ok());
let file: FsFile = ftp
.list_dir(PathBuf::from("/uploads").as_path())
.ok()
.unwrap()
.get(0)
.unwrap()
.clone()
.unwrap_file();
let mut readable = ftp.recv_file(&file).ok().unwrap();
let mut data: Vec<u8> = vec![0; 1024];
assert!(readable.read(&mut data).is_ok());
assert!(ftp.on_recv(readable).is_ok());
// Receive file (err)
assert!(ftp.recv_file(&entry).is_err());
// Cleanup
assert!(ftp.change_dir(PathBuf::from("/").as_path()).is_ok());
assert!(ftp
.remove(&make_fsentry(PathBuf::from("/home"), true))
.is_ok());
assert!(ftp
.remove(&make_fsentry(PathBuf::from("/uploads"), true))
.is_ok());
// Disconnect
assert!(ftp.disconnect().is_ok());
assert_eq!(ftp.is_connected(), false);
}
#[test]
#[cfg(feature = "with-containers")]
fn test_filetransfer_ftp_server_bad_auth() {
let mut ftp: FtpFileTransfer = FtpFileTransfer::new(false);
// Connect
assert!(ftp
.connect(
String::from("127.0.0.1"),
10021,
Some(String::from("omar")),
Some(String::from("ommlar")),
)
.is_err());
}
#[test]
#[cfg(feature = "with-containers")]
fn test_filetransfer_ftp_no_credentials() {
let mut ftp: FtpFileTransfer = FtpFileTransfer::new(false);
assert!(ftp
.connect(String::from("127.0.0.1"), 10021, None, None)
.is_err());
}
#[test]
fn test_filetransfer_ftp_server_bad_server() {
let mut ftp: FtpFileTransfer = FtpFileTransfer::new(false);
// Connect
assert!(ftp
.connect(
String::from("mybadserver.veryverybad.awful"),
21,
Some(String::from("omar")),
Some(String::from("ommlar")),
)
.is_err());
}
#[test] #[test]
fn test_filetransfer_ftp_parse_list_line_unix() { fn test_filetransfer_ftp_parse_list_line_unix() {
let mut ftp: FtpFileTransfer = FtpFileTransfer::new(false); let mut ftp: FtpFileTransfer = FtpFileTransfer::new(false);
// Simple file // Simple file
let fs_entry: FsEntry = ftp let file: FsFile = ftp
.parse_list_line( .parse_list_line(
PathBuf::from("/tmp").as_path(), PathBuf::from("/tmp").as_path(),
"-rw-rw-r-- 1 root dialout 8192 Nov 5 2018 omar.txt", "-rw-rw-r-- 1 root dialout 8192 Nov 5 2018 omar.txt",
) )
.ok() .ok()
.unwrap(); .unwrap()
if let FsEntry::File(file) = fs_entry { .unwrap_file();
assert_eq!(file.abs_path, PathBuf::from("/tmp/omar.txt")); assert_eq!(file.abs_path, PathBuf::from("/tmp/omar.txt"));
assert_eq!(file.name, String::from("omar.txt")); assert_eq!(file.name, String::from("omar.txt"));
assert_eq!(file.size, 8192); assert_eq!(file.size, 8192);
assert!(file.symlink.is_none()); assert!(file.symlink.is_none());
assert_eq!(file.user, None); assert_eq!(file.user, None);
assert_eq!(file.group, None); assert_eq!(file.group, None);
assert_eq!(file.unix_pex.unwrap(), (6, 6, 4)); assert_eq!(file.unix_pex.unwrap(), (6, 6, 4));
assert_eq!( assert_eq!(
file.last_access_time file.last_access_time
.duration_since(SystemTime::UNIX_EPOCH) .duration_since(SystemTime::UNIX_EPOCH)
.ok() .ok()
.unwrap(), .unwrap(),
Duration::from_secs(1541376000) Duration::from_secs(1541376000)
); );
assert_eq!( assert_eq!(
file.last_change_time file.last_change_time
.duration_since(SystemTime::UNIX_EPOCH) .duration_since(SystemTime::UNIX_EPOCH)
.ok() .ok()
.unwrap(), .unwrap(),
Duration::from_secs(1541376000) Duration::from_secs(1541376000)
); );
assert_eq!( assert_eq!(
file.creation_time file.creation_time
.duration_since(SystemTime::UNIX_EPOCH) .duration_since(SystemTime::UNIX_EPOCH)
.ok() .ok()
.unwrap(), .unwrap(),
Duration::from_secs(1541376000) Duration::from_secs(1541376000)
); );
} else {
panic!("Expected file, got directory");
}
// Simple file with number as gid, uid // Simple file with number as gid, uid
let fs_entry: FsEntry = ftp let file: FsFile = ftp
.parse_list_line( .parse_list_line(
PathBuf::from("/tmp").as_path(), PathBuf::from("/tmp").as_path(),
"-rwxr-xr-x 1 0 9 4096 Nov 5 16:32 omar.txt", "-rwxr-xr-x 1 0 9 4096 Nov 5 16:32 omar.txt",
) )
.ok() .ok()
.unwrap(); .unwrap()
if let FsEntry::File(file) = fs_entry { .unwrap_file();
assert_eq!(file.abs_path, PathBuf::from("/tmp/omar.txt")); assert_eq!(file.abs_path, PathBuf::from("/tmp/omar.txt"));
assert_eq!(file.name, String::from("omar.txt")); assert_eq!(file.name, String::from("omar.txt"));
assert_eq!(file.size, 4096); assert_eq!(file.size, 4096);
assert!(file.symlink.is_none()); assert!(file.symlink.is_none());
assert_eq!(file.user, Some(0)); assert_eq!(file.user, Some(0));
assert_eq!(file.group, Some(9)); assert_eq!(file.group, Some(9));
assert_eq!(file.unix_pex.unwrap(), (7, 5, 5)); assert_eq!(file.unix_pex.unwrap(), (7, 5, 5));
assert_eq!( assert_eq!(
fmt_time(file.last_access_time, "%m %d %M").as_str(), fmt_time(file.last_access_time, "%m %d %M").as_str(),
"11 05 32" "11 05 32"
); );
assert_eq!( assert_eq!(
fmt_time(file.last_change_time, "%m %d %M").as_str(), fmt_time(file.last_change_time, "%m %d %M").as_str(),
"11 05 32" "11 05 32"
); );
assert_eq!( assert_eq!(
fmt_time(file.creation_time, "%m %d %M").as_str(), fmt_time(file.creation_time, "%m %d %M").as_str(),
"11 05 32" "11 05 32"
); );
} else {
panic!("Expected file, got directory");
}
// Directory // Directory
let fs_entry: FsEntry = ftp let dir: FsDirectory = ftp
.parse_list_line( .parse_list_line(
PathBuf::from("/tmp").as_path(), PathBuf::from("/tmp").as_path(),
"drwxrwxr-x 1 0 9 4096 Nov 5 2018 docs", "drwxrwxr-x 1 0 9 4096 Nov 5 2018 docs",
) )
.ok() .ok()
.unwrap(); .unwrap()
if let FsEntry::Directory(dir) = fs_entry { .unwrap_dir();
assert_eq!(dir.abs_path, PathBuf::from("/tmp/docs")); assert_eq!(dir.abs_path, PathBuf::from("/tmp/docs"));
assert_eq!(dir.name, String::from("docs")); assert_eq!(dir.name, String::from("docs"));
assert!(dir.symlink.is_none()); assert!(dir.symlink.is_none());
assert_eq!(dir.user, Some(0)); assert_eq!(dir.user, Some(0));
assert_eq!(dir.group, Some(9)); assert_eq!(dir.group, Some(9));
assert_eq!(dir.unix_pex.unwrap(), (7, 7, 5)); assert_eq!(dir.unix_pex.unwrap(), (7, 7, 5));
assert_eq!( assert_eq!(
dir.last_access_time dir.last_access_time
.duration_since(SystemTime::UNIX_EPOCH) .duration_since(SystemTime::UNIX_EPOCH)
.ok() .ok()
.unwrap(), .unwrap(),
Duration::from_secs(1541376000) Duration::from_secs(1541376000)
); );
assert_eq!( assert_eq!(
dir.last_change_time dir.last_change_time
.duration_since(SystemTime::UNIX_EPOCH) .duration_since(SystemTime::UNIX_EPOCH)
.ok() .ok()
.unwrap(), .unwrap(),
Duration::from_secs(1541376000) Duration::from_secs(1541376000)
); );
assert_eq!( assert_eq!(
dir.creation_time dir.creation_time
.duration_since(SystemTime::UNIX_EPOCH) .duration_since(SystemTime::UNIX_EPOCH)
.ok() .ok()
.unwrap(), .unwrap(),
Duration::from_secs(1541376000) Duration::from_secs(1541376000)
); );
assert_eq!(dir.readonly, false); assert_eq!(dir.readonly, false);
} else {
panic!("Expected directory, got directory");
}
// Error // Error
assert!(ftp assert!(ftp
.parse_list_line( .parse_list_line(
@ -1002,186 +1171,85 @@ mod tests {
fn test_filetransfer_ftp_parse_list_line_dos() { fn test_filetransfer_ftp_parse_list_line_dos() {
let mut ftp: FtpFileTransfer = FtpFileTransfer::new(false); let mut ftp: FtpFileTransfer = FtpFileTransfer::new(false);
// Simple file // Simple file
let fs_entry: FsEntry = ftp let file: FsFile = ftp
.parse_list_line( .parse_list_line(
PathBuf::from("/tmp").as_path(), PathBuf::from("/tmp").as_path(),
"04-08-14 03:09PM 8192 omar.txt", "04-08-14 03:09PM 8192 omar.txt",
) )
.ok() .ok()
.unwrap(); .unwrap()
if let FsEntry::File(file) = fs_entry { .unwrap_file();
assert_eq!(file.abs_path, PathBuf::from("/tmp/omar.txt")); assert_eq!(file.abs_path, PathBuf::from("/tmp/omar.txt"));
assert_eq!(file.name, String::from("omar.txt")); assert_eq!(file.name, String::from("omar.txt"));
assert_eq!(file.size, 8192); assert_eq!(file.size, 8192);
assert!(file.symlink.is_none()); assert!(file.symlink.is_none());
assert_eq!(file.user, None); assert_eq!(file.user, None);
assert_eq!(file.group, None); assert_eq!(file.group, None);
assert_eq!(file.unix_pex, None); assert_eq!(file.unix_pex, None);
assert_eq!( assert_eq!(
file.last_access_time file.last_access_time
.duration_since(SystemTime::UNIX_EPOCH) .duration_since(SystemTime::UNIX_EPOCH)
.ok() .ok()
.unwrap(), .unwrap(),
Duration::from_secs(1407164940) Duration::from_secs(1407164940)
); );
assert_eq!( assert_eq!(
file.last_change_time file.last_change_time
.duration_since(SystemTime::UNIX_EPOCH) .duration_since(SystemTime::UNIX_EPOCH)
.ok() .ok()
.unwrap(), .unwrap(),
Duration::from_secs(1407164940) Duration::from_secs(1407164940)
); );
assert_eq!( assert_eq!(
file.creation_time file.creation_time
.duration_since(SystemTime::UNIX_EPOCH) .duration_since(SystemTime::UNIX_EPOCH)
.ok() .ok()
.unwrap(), .unwrap(),
Duration::from_secs(1407164940) Duration::from_secs(1407164940)
); );
} else {
panic!("Expected file, got directory");
}
// Directory // Directory
let fs_entry: FsEntry = ftp let dir: FsDirectory = ftp
.parse_list_line( .parse_list_line(
PathBuf::from("/tmp").as_path(), PathBuf::from("/tmp").as_path(),
"04-08-14 03:09PM <DIR> docs", "04-08-14 03:09PM <DIR> docs",
) )
.ok() .ok()
.unwrap(); .unwrap()
if let FsEntry::Directory(dir) = fs_entry { .unwrap_dir();
assert_eq!(dir.abs_path, PathBuf::from("/tmp/docs")); assert_eq!(dir.abs_path, PathBuf::from("/tmp/docs"));
assert_eq!(dir.name, String::from("docs")); assert_eq!(dir.name, String::from("docs"));
assert!(dir.symlink.is_none()); assert!(dir.symlink.is_none());
assert_eq!(dir.user, None); assert_eq!(dir.user, None);
assert_eq!(dir.group, None); assert_eq!(dir.group, None);
assert_eq!(dir.unix_pex, None); assert_eq!(dir.unix_pex, None);
assert_eq!( assert_eq!(
dir.last_access_time dir.last_access_time
.duration_since(SystemTime::UNIX_EPOCH) .duration_since(SystemTime::UNIX_EPOCH)
.ok() .ok()
.unwrap(), .unwrap(),
Duration::from_secs(1407164940) Duration::from_secs(1407164940)
); );
assert_eq!( assert_eq!(
dir.last_change_time dir.last_change_time
.duration_since(SystemTime::UNIX_EPOCH) .duration_since(SystemTime::UNIX_EPOCH)
.ok() .ok()
.unwrap(), .unwrap(),
Duration::from_secs(1407164940) Duration::from_secs(1407164940)
); );
assert_eq!( assert_eq!(
dir.creation_time dir.creation_time
.duration_since(SystemTime::UNIX_EPOCH) .duration_since(SystemTime::UNIX_EPOCH)
.ok() .ok()
.unwrap(), .unwrap(),
Duration::from_secs(1407164940) Duration::from_secs(1407164940)
); );
assert_eq!(dir.readonly, false); assert_eq!(dir.readonly, false);
} else {
panic!("Expected directory, got directory");
}
// Error // Error
assert!(ftp assert!(ftp
.parse_list_line(PathBuf::from("/").as_path(), "04-08-14 omar.txt") .parse_list_line(PathBuf::from("/").as_path(), "04-08-14 omar.txt")
.is_err()); .is_err());
} }
#[test]
fn test_filetransfer_ftp_connect_unsecure_anonymous() {
let mut ftp: FtpFileTransfer = FtpFileTransfer::new(false);
// Connect
assert!(ftp
.connect(String::from("speedtest.tele2.net"), 21, None, None)
.is_ok());
// Pwd
assert_eq!(ftp.pwd().ok().unwrap(), PathBuf::from("/"));
// Disconnect
assert!(ftp.disconnect().is_ok());
}
#[test]
fn test_filetransfer_ftp_connect_unsecure_username() {
let mut ftp: FtpFileTransfer = FtpFileTransfer::new(false);
// Connect
assert!(ftp
.connect(
String::from("test.rebex.net"),
21,
Some(String::from("demo")),
Some(String::from("password"))
)
.is_ok());
// Pwd
assert_eq!(ftp.pwd().ok().unwrap(), PathBuf::from("/"));
// Disconnect
assert!(ftp.disconnect().is_ok());
}
#[test]
fn test_filetransfer_ftp_connect_secure() {
let mut ftp: FtpFileTransfer = FtpFileTransfer::new(true);
// Connect
assert!(ftp
.connect(
String::from("test.rebex.net"),
21,
Some(String::from("demo")),
Some(String::from("password"))
)
.is_ok());
// Pwd
assert_eq!(ftp.pwd().ok().unwrap(), PathBuf::from("/"));
// Disconnect
assert!(ftp.disconnect().is_ok());
}
#[test]
fn test_filetransfer_ftp_change_dir() {
let mut ftp: FtpFileTransfer = FtpFileTransfer::new(false);
// Connect
assert!(ftp
.connect(String::from("speedtest.tele2.net"), 21, None, None)
.is_ok());
// Pwd
assert_eq!(ftp.pwd().ok().unwrap(), PathBuf::from("/"));
// Cwd
assert!(ftp.change_dir(PathBuf::from("upload/").as_path()).is_ok());
// Pwd
assert_eq!(ftp.pwd().ok().unwrap(), PathBuf::from("/upload"));
// Disconnect
assert!(ftp.disconnect().is_ok());
}
#[test]
fn test_filetransfer_ftp_copy() {
let mut ftp: FtpFileTransfer = FtpFileTransfer::new(false);
// Connect
assert!(ftp
.connect(String::from("speedtest.tele2.net"), 21, None, None)
.is_ok());
// Pwd
assert_eq!(ftp.pwd().ok().unwrap(), PathBuf::from("/"));
// Copy
let file: FsFile = FsFile {
name: String::from("readme.txt"),
abs_path: PathBuf::from("/readme.txt"),
last_change_time: SystemTime::UNIX_EPOCH,
last_access_time: SystemTime::UNIX_EPOCH,
creation_time: SystemTime::UNIX_EPOCH,
size: 0,
ftype: Some(String::from("txt")), // File type
readonly: true,
symlink: None, // UNIX only
user: Some(0), // UNIX only
group: Some(0), // UNIX only
unix_pex: Some((6, 4, 4)), // UNIX only
};
assert!(ftp
.copy(&FsEntry::File(file), &Path::new("/tmp/dest.txt"))
.is_err());
}
#[test] #[test]
fn test_filetransfer_ftp_list_dir_dos_syntax() { fn test_filetransfer_ftp_list_dir_dos_syntax() {
let mut ftp: FtpFileTransfer = FtpFileTransfer::new(false); let mut ftp: FtpFileTransfer = FtpFileTransfer::new(false);
@ -1205,94 +1273,16 @@ mod tests {
} }
#[test] #[test]
#[cfg(not(target_os = "macos"))] fn test_filetransfer_ftp_get_name_and_link() {
fn test_filetransfer_ftp_list_dir_unix_syntax() { let client: FtpFileTransfer = FtpFileTransfer::new(false);
let mut ftp: FtpFileTransfer = FtpFileTransfer::new(false); assert_eq!(
// Connect client.get_name_and_link("Cargo.toml"),
assert!(ftp (String::from("Cargo.toml"), None)
.connect(String::from("speedtest.tele2.net"), 21, None, None) );
.is_ok()); assert_eq!(
// Pwd client.get_name_and_link("Cargo -> Cargo.toml"),
assert_eq!(ftp.pwd().ok().unwrap(), PathBuf::from("/")); (String::from("Cargo"), Some(PathBuf::from("Cargo.toml")))
// List dir );
let files: Vec<FsEntry> = ftp.list_dir(PathBuf::from("/").as_path()).ok().unwrap();
// There should be at least 1 file
assert!(files.len() > 0);
// Disconnect
assert!(ftp.disconnect().is_ok());
}
/* NOTE: they don't work
#[test]
fn test_filetransfer_ftp_recv() {
let mut ftp: FtpFileTransfer = FtpFileTransfer::new(false);
// Connect
assert!(ftp.connect(String::from("test.rebex.net"), 21, Some(String::from("demo")), Some(String::from("password"))).is_ok());
// Pwd
assert_eq!(ftp.pwd().ok().unwrap(), PathBuf::from("/"));
// Recv 100KB
assert!(ftp.recv_file(PathBuf::from("readme.txt").as_path()).is_ok());
// Disconnect
assert!(ftp.disconnect().is_ok());
}
#[test]
fn test_filetransfer_ftp_send() {
let mut ftp: FtpFileTransfer = FtpFileTransfer::new(false);
// Connect
assert!(ftp.connect(String::from("speedtest.tele2.net"), 21, None, None).is_ok());
// Pwd
assert_eq!(ftp.pwd().ok().unwrap(), PathBuf::from("/"));
// Cwd
assert!(ftp.change_dir(PathBuf::from("upload/").as_path()).is_ok());
// Pwd
assert_eq!(ftp.pwd().ok().unwrap(), PathBuf::from("/upload"));
// Send a sample file 100KB
assert!(ftp.send_file(PathBuf::from("test.txt").as_path()).is_ok());
// Disconnect
assert!(ftp.disconnect().is_ok());
}*/
#[test]
fn test_filetransfer_ftp_exec() {
let mut ftp: FtpFileTransfer = FtpFileTransfer::new(false);
// Connect
assert!(ftp
.connect(String::from("speedtest.tele2.net"), 21, None, None)
.is_ok());
// Pwd
assert!(ftp.exec("echo 1;").is_err());
// Disconnect
assert!(ftp.disconnect().is_ok());
}
#[test]
fn test_filetransfer_ftp_find() {
let mut client: FtpFileTransfer = FtpFileTransfer::new(false);
// Connect
assert!(client
.connect(
String::from("test.rebex.net"),
21,
Some(String::from("demo")),
Some(String::from("password"))
)
.is_ok());
// Pwd
assert_eq!(client.pwd().ok().unwrap(), PathBuf::from("/"));
// Search for file (let's search for pop3-*.png); there should be 2
let search_res: Vec<FsEntry> = client.find("pop3-*.png").ok().unwrap();
assert_eq!(search_res.len(), 2);
// verify names
assert_eq!(search_res[0].get_name(), "pop3-browser.png");
assert_eq!(search_res[1].get_name(), "pop3-console-client.png");
// Search directory
let search_res: Vec<FsEntry> = client.find("pub").ok().unwrap();
assert_eq!(search_res.len(), 1);
// Disconnect
assert!(client.disconnect().is_ok());
// Verify err
assert!(client.find("pippo").is_err());
} }
#[test] #[test]
@ -1316,9 +1306,25 @@ mod tests {
assert!(ftp.disconnect().is_err()); assert!(ftp.disconnect().is_err());
assert!(ftp.list_dir(Path::new("/tmp")).is_err()); assert!(ftp.list_dir(Path::new("/tmp")).is_err());
assert!(ftp.mkdir(Path::new("/tmp")).is_err()); assert!(ftp.mkdir(Path::new("/tmp")).is_err());
assert!(ftp
.remove(&make_fsentry(PathBuf::from("/nowhere"), false))
.is_err());
assert!(ftp
.rename(
&make_fsentry(PathBuf::from("/nowhere"), false),
PathBuf::from("/culonia").as_path()
)
.is_err());
assert!(ftp.pwd().is_err()); assert!(ftp.pwd().is_err());
assert!(ftp.stat(Path::new("/tmp")).is_err()); assert!(ftp.stat(Path::new("/tmp")).is_err());
assert!(ftp.recv_file(&file).is_err()); assert!(ftp.recv_file(&file).is_err());
assert!(ftp.send_file(&file, Path::new("/tmp/omar.txt")).is_err()); assert!(ftp.send_file(&file, Path::new("/tmp/omar.txt")).is_err());
let (_, temp): (FsFile, tempfile::NamedTempFile) = create_sample_file_entry();
let readable: Box<dyn Read> = Box::new(std::fs::File::open(temp.path()).unwrap());
assert!(ftp.on_recv(readable).is_err());
let (_, temp): (FsFile, tempfile::NamedTempFile) = create_sample_file_entry();
let writable: Box<dyn Write> =
Box::new(open_file(temp.path(), true, true, true).ok().unwrap());
assert!(ftp.on_sent(writable).is_err());
} }
} }

View File

@ -284,10 +284,7 @@ pub trait FileTransfer {
if filter.matches(dir.name.as_str()) { if filter.matches(dir.name.as_str()) {
drained.push(FsEntry::Directory(dir.clone())); drained.push(FsEntry::Directory(dir.clone()));
} }
match self.iter_search(dir.abs_path.as_path(), filter) { drained.append(&mut self.iter_search(dir.abs_path.as_path(), filter)?);
Ok(mut filtered) => drained.append(&mut filtered),
Err(err) => return Err(err),
}
} }
FsEntry::File(file) => { FsEntry::File(file) => {
if filter.matches(file.name.as_str()) { if filter.matches(file.name.as_str()) {

View File

@ -445,10 +445,9 @@ impl FileTransfer for ScpFileTransfer {
self.session = Some(session); self.session = Some(session);
// Get working directory // Get working directory
debug!("Getting working directory..."); debug!("Getting working directory...");
match self.perform_shell_cmd("pwd") { self.wrkdir = self
Ok(output) => self.wrkdir = PathBuf::from(output.as_str().trim()), .perform_shell_cmd("pwd")
Err(err) => return Err(err), .map(|x| PathBuf::from(x.as_str().trim()))?;
}
info!( info!(
"Connection established; working directory: {}", "Connection established; working directory: {}",
self.wrkdir.display() self.wrkdir.display()
@ -486,7 +485,7 @@ impl FileTransfer for ScpFileTransfer {
/// ///
/// Indicates whether the client is connected to remote /// Indicates whether the client is connected to remote
fn is_connected(&self) -> bool { fn is_connected(&self) -> bool {
self.session.as_ref().is_some() self.session.is_some()
} }
/// ### pwd /// ### pwd
@ -853,7 +852,15 @@ impl FileTransfer for ScpFileTransfer {
) -> Result<Box<dyn Write>, FileTransferError> { ) -> Result<Box<dyn Write>, FileTransferError> {
match self.session.as_ref() { match self.session.as_ref() {
Some(session) => { Some(session) => {
let file_name: PathBuf = Self::resolve(file_name); let file_name: PathBuf = match file_name.is_absolute() {
true => PathBuf::from(file_name),
false => {
let mut p: PathBuf = self.wrkdir.clone();
p.push(file_name);
Self::resolve(p.as_path())
}
};
let file_name: PathBuf = Self::resolve(file_name.as_path());
info!( info!(
"Sending file {} to {}", "Sending file {} to {}",
local.abs_path.display(), local.abs_path.display(),
@ -963,8 +970,12 @@ impl FileTransfer for ScpFileTransfer {
mod tests { mod tests {
use super::*; use super::*;
use crate::utils::test_helpers::make_fsentry;
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
#[cfg(feature = "with-containers")]
use crate::utils::test_helpers::{create_sample_file_entry, write_file, write_ssh_key};
#[test] #[test]
fn test_filetransfer_scp_new() { fn test_filetransfer_scp_new() {
let client: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty()); let client: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty());
@ -973,31 +984,210 @@ mod tests {
} }
#[test] #[test]
fn test_filetransfer_scp_connect() { #[cfg(feature = "with-containers")]
fn test_filetransfer_scp_server() {
let mut client: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty()); let mut client: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty());
assert_eq!(client.is_connected(), false); // Sample file
let (entry, file): (FsFile, tempfile::NamedTempFile) = create_sample_file_entry();
// Connect
assert!(client assert!(client
.connect( .connect(
String::from("test.rebex.net"), String::from("127.0.0.1"),
22, 10222,
Some(String::from("demo")), Some(String::from("sftp")),
Some(String::from("password")) Some(String::from("password"))
) )
.is_ok()); .is_ok());
// Check session and scp // Check session and sftp
assert!(client.session.is_some()); assert!(client.session.is_some());
assert_eq!(client.wrkdir, PathBuf::from("/config"));
assert_eq!(client.is_connected(), true); assert_eq!(client.is_connected(), true);
// Pwd
assert_eq!(client.wrkdir.clone(), client.pwd().ok().unwrap());
// Stat
let stat: FsFile = client
.stat(PathBuf::from("sshd.pid").as_path())
.ok()
.unwrap()
.unwrap_file();
assert_eq!(stat.abs_path, PathBuf::from("/config/sshd.pid"));
let stat: FsDirectory = client
.stat(PathBuf::from("/config/").as_path())
.ok()
.unwrap()
.unwrap_dir();
assert_eq!(stat.abs_path, PathBuf::from("/config/"));
// Stat (err)
assert!(client
.stat(PathBuf::from("/config/5t0ca220.log").as_path())
.is_err());
// List dir (dir has 4 (one is hidden :D) entries)
assert_eq!(client.list_dir(&Path::new("/config")).unwrap().len(), 4);
// Make directory
assert!(client.mkdir(PathBuf::from("/tmp/omar").as_path()).is_ok());
// Make directory (err)
assert!(client
.mkdir(PathBuf::from("/root/aaaaa/pommlar").as_path())
.is_err());
// Change directory
assert!(client
.change_dir(PathBuf::from("/tmp/omar").as_path())
.is_ok());
// Change directory (err)
assert!(client
.change_dir(PathBuf::from("/tmp/oooo/aaaa/eee").as_path())
.is_err());
// Copy file
assert!(client
.copy(
&make_fsentry(PathBuf::from("/config/sshd.pid"), false),
PathBuf::from("/tmp/sshd.pid").as_path()
)
.is_ok());
// Copy dir
assert!(client
.copy(
&make_fsentry(PathBuf::from("/tmp/omar"), true),
PathBuf::from("/tmp/ommlar").as_path()
)
.is_ok());
// Copy (err)
assert!(client
.copy(
&make_fsentry(PathBuf::from("/tmp/zattera"), false),
PathBuf::from("/").as_path()
)
.is_err());
// Exec
assert_eq!(client.exec("echo 5").ok().unwrap().as_str(), "5\n");
// Change dir to ommlar
assert!(client
.change_dir(PathBuf::from("/tmp/ommlar/").as_path())
.is_ok());
// Upload 2 files
let mut writable = client
.send_file(&entry, PathBuf::from("omar.txt").as_path())
.ok()
.unwrap();
write_file(&file, &mut writable);
assert!(client.on_sent(writable).is_ok());
let mut writable = client
.send_file(&entry, PathBuf::from("README.md").as_path())
.ok()
.unwrap();
write_file(&file, &mut writable);
assert!(client.on_sent(writable).is_ok());
// Upload file (err)
assert!(client
.send_file(&entry, PathBuf::from("/ommlar/omarone").as_path())
.is_err());
// List dir
let list: Vec<FsEntry> = client
.list_dir(PathBuf::from("/tmp/ommlar").as_path())
.ok()
.unwrap();
assert_eq!(list.len(), 2);
// Find
assert_eq!(client.find("*.txt").ok().unwrap().len(), 1);
assert_eq!(client.find("*.md").ok().unwrap().len(), 1);
assert_eq!(client.find("*.jpeg").ok().unwrap().len(), 0);
// Rename
assert!(client
.mkdir(PathBuf::from("/tmp/uploads").as_path())
.is_ok());
assert!(client
.rename(
list.get(0).unwrap(),
PathBuf::from("/tmp/uploads/README.txt").as_path()
)
.is_ok());
// Rename (err)
assert!(client
.rename(list.get(0).unwrap(), PathBuf::from("OMARONE").as_path())
.is_err());
let dummy: FsEntry = FsEntry::File(FsFile {
name: String::from("cucumber.txt"),
abs_path: PathBuf::from("/cucumber.txt"),
last_change_time: SystemTime::UNIX_EPOCH,
last_access_time: SystemTime::UNIX_EPOCH,
creation_time: SystemTime::UNIX_EPOCH,
size: 0,
ftype: Some(String::from("txt")), // File type
readonly: true,
symlink: None, // UNIX only
user: Some(0), // UNIX only
group: Some(0), // UNIX only
unix_pex: Some((6, 4, 4)), // UNIX only
});
assert!(client
.rename(&dummy, PathBuf::from("/a/b/c").as_path())
.is_err());
// Remove
assert!(client.remove(list.get(1).unwrap()).is_ok());
// Receive file
let mut writable = client
.send_file(&entry, PathBuf::from("/tmp/uploads/README.txt").as_path())
.ok()
.unwrap();
write_file(&file, &mut writable);
assert!(client.on_sent(writable).is_ok());
let file: FsFile = client
.list_dir(PathBuf::from("/tmp/uploads").as_path())
.ok()
.unwrap()
.get(0)
.unwrap()
.clone()
.unwrap_file();
let mut readable = client.recv_file(&file).ok().unwrap();
let mut data: Vec<u8> = vec![0; 1024];
assert!(readable.read(&mut data).is_ok());
assert!(client.on_recv(readable).is_ok());
// Receive file (err)
assert!(client.recv_file(&entry).is_err());
// Cleanup
assert!(client.change_dir(PathBuf::from("/").as_path()).is_ok());
assert!(client
.remove(&make_fsentry(PathBuf::from("/tmp/ommlar"), true))
.is_ok());
assert!(client
.remove(&make_fsentry(PathBuf::from("/tmp/omar"), true))
.is_ok());
assert!(client
.remove(&make_fsentry(PathBuf::from("/tmp/uploads"), true))
.is_ok());
// Disconnect // Disconnect
assert!(client.disconnect().is_ok()); assert!(client.disconnect().is_ok());
assert_eq!(client.is_connected(), false); assert_eq!(client.is_connected(), false);
} }
#[test]
#[cfg(feature = "with-containers")]
fn test_filetransfer_scp_ssh_storage() {
let mut storage: SshKeyStorage = SshKeyStorage::empty();
let key_file: tempfile::NamedTempFile = write_ssh_key();
storage.add_key("127.0.0.1", "sftp", key_file.path().to_path_buf());
let mut client: ScpFileTransfer = ScpFileTransfer::new(storage);
// Connect
assert!(client
.connect(
String::from("127.0.0.1"),
10222,
Some(String::from("sftp")),
None,
)
.is_ok());
assert_eq!(client.is_connected(), true);
assert!(client.disconnect().is_ok());
}
#[test] #[test]
fn test_filetransfer_scp_bad_auth() { fn test_filetransfer_scp_bad_auth() {
let mut client: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty()); let mut client: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty());
assert!(client assert!(client
.connect( .connect(
String::from("test.rebex.net"), String::from("127.0.0.1"),
22, 10222,
Some(String::from("demo")), Some(String::from("demo")),
Some(String::from("badpassword")) Some(String::from("badpassword"))
) )
@ -1005,10 +1195,11 @@ mod tests {
} }
#[test] #[test]
#[cfg(feature = "with-containers")]
fn test_filetransfer_scp_no_credentials() { fn test_filetransfer_scp_no_credentials() {
let mut client: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty()); let mut client: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty());
assert!(client assert!(client
.connect(String::from("test.rebex.net"), 22, None, None) .connect(String::from("127.0.0.1"), 10222, None, None)
.is_err()); .is_err());
} }
@ -1024,245 +1215,92 @@ mod tests {
) )
.is_err()); .is_err());
} }
#[test]
fn test_filetransfer_scp_pwd() {
let mut client: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty());
assert!(client
.connect(
String::from("test.rebex.net"),
22,
Some(String::from("demo")),
Some(String::from("password"))
)
.is_ok());
// Check session and scp
assert!(client.session.is_some());
// Pwd
assert_eq!(client.pwd().ok().unwrap(), PathBuf::from("/"));
// Disconnect
assert!(client.disconnect().is_ok());
}
#[test] #[test]
#[cfg(any(target_os = "unix", target_os = "macos", target_os = "linux"))] fn test_filetransfer_scp_parse_ls() {
fn test_filetransfer_scp_cwd() {
let mut client: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty()); let mut client: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty());
assert!(client // File
.connect( let entry: FsFile = client
String::from("test.rebex.net"), .parse_ls_output(
22, PathBuf::from("/tmp").as_path(),
Some(String::from("demo")), "-rw-r--r-- 1 root root 2056 giu 13 21:11 Cargo.toml",
Some(String::from("password"))
) )
.is_ok());
// Check session and scp
assert!(client.session.is_some());
// Cwd (relative)
assert!(client.change_dir(PathBuf::from("pub/").as_path()).is_ok());
// Cwd (absolute)
assert!(client.change_dir(PathBuf::from("/pub").as_path()).is_ok());
// Disconnect
assert!(client.disconnect().is_ok());
}
#[test]
fn test_filetransfer_scp_cwd_error() {
let mut client: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty());
assert!(client
.connect(
String::from("test.rebex.net"),
22,
Some(String::from("demo")),
Some(String::from("password"))
)
.is_ok());
// Cwd (abs)
assert!(client
.change_dir(PathBuf::from("/omar/gabber").as_path())
.is_err());
// Cwd (rel)
assert!(client
.change_dir(PathBuf::from("gomar/pett").as_path())
.is_err());
// Disconnect
assert!(client.disconnect().is_ok());
}
#[test]
fn test_filetransfer_scp_ls() {
let mut client: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty());
assert!(client
.connect(
String::from("test.rebex.net"),
22,
Some(String::from("demo")),
Some(String::from("password"))
)
.is_ok());
// Check session and scp
assert!(client.session.is_some());
// List dir
let pwd: PathBuf = client.pwd().ok().unwrap();
let files: Vec<FsEntry> = client.list_dir(pwd.as_path()).ok().unwrap();
assert_eq!(files.len(), 3); // There are 3 files
// Disconnect
assert!(client.disconnect().is_ok());
}
#[test]
fn test_filetransfer_scp_stat() {
let mut client: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty());
assert!(client
.connect(
String::from("test.rebex.net"),
22,
Some(String::from("demo")),
Some(String::from("password"))
)
.is_ok());
// Check session and scp
assert!(client.session.is_some());
let file: FsEntry = client
.stat(PathBuf::from("readme.txt").as_path())
.ok() .ok()
.unwrap(); .unwrap()
if let FsEntry::File(file) = file { .unwrap_file();
assert_eq!(file.abs_path, PathBuf::from("/readme.txt")); assert_eq!(entry.name.as_str(), "Cargo.toml");
} else { assert_eq!(entry.abs_path, PathBuf::from("/tmp/Cargo.toml"));
panic!("Expected readme.txt to be a file"); assert_eq!(entry.unix_pex.unwrap(), (6, 4, 4));
} assert_eq!(entry.size, 2056);
assert_eq!(entry.readonly, false);
assert_eq!(entry.ftype.unwrap().as_str(), "toml");
assert!(entry.symlink.is_none());
// File (year)
let entry: FsFile = client
.parse_ls_output(
PathBuf::from("/tmp").as_path(),
"-rw-rw-rw- 1 root root 3368 nov 7 2020 CODE_OF_CONDUCT.md",
)
.ok()
.unwrap()
.unwrap_file();
assert_eq!(entry.name.as_str(), "CODE_OF_CONDUCT.md");
assert_eq!(entry.abs_path, PathBuf::from("/tmp/CODE_OF_CONDUCT.md"));
assert_eq!(entry.unix_pex.unwrap(), (6, 6, 6));
assert_eq!(entry.size, 3368);
assert_eq!(entry.readonly, false);
assert_eq!(entry.ftype.unwrap().as_str(), "md");
assert!(entry.symlink.is_none());
// Directory
let entry: FsDirectory = client
.parse_ls_output(
PathBuf::from("/tmp").as_path(),
"drwxr-xr-x 1 root root 512 giu 13 21:11 docs",
)
.ok()
.unwrap()
.unwrap_dir();
assert_eq!(entry.name.as_str(), "docs");
assert_eq!(entry.abs_path, PathBuf::from("/tmp/docs"));
assert_eq!(entry.unix_pex.unwrap(), (7, 5, 5));
assert_eq!(entry.readonly, false);
assert!(entry.symlink.is_none());
// Short metadata
assert!(client
.parse_ls_output(
PathBuf::from("/tmp").as_path(),
"drwxr-xr-x 1 root root 512 giu 13 21:11",
)
.is_err());
// Special file
assert!(client
.parse_ls_output(
PathBuf::from("/tmp").as_path(),
"crwxr-xr-x 1 root root 512 giu 13 21:11 ttyS1",
)
.is_err());
// Bad pex
assert!(client
.parse_ls_output(
PathBuf::from("/tmp").as_path(),
"-rwxr-xr 1 root root 512 giu 13 21:11 ttyS1",
)
.is_err());
} }
#[test] #[test]
fn test_filetransfer_scp_exec() { fn test_filetransfer_scp_get_name_and_link() {
let mut client: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty()); let client: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty());
assert!(client assert_eq!(
.connect( client.get_name_and_link("Cargo.toml"),
String::from("test.rebex.net"), (String::from("Cargo.toml"), None)
22, );
Some(String::from("demo")), assert_eq!(
Some(String::from("password")) client.get_name_and_link("Cargo -> Cargo.toml"),
) (String::from("Cargo"), Some(PathBuf::from("Cargo.toml")))
.is_ok()); );
// Check session and scp
assert!(client.session.is_some());
// Exec
assert_eq!(client.exec("echo 5").ok().unwrap().as_str(), "5\n");
// Disconnect
assert!(client.disconnect().is_ok());
} }
#[test]
//#[cfg(any(target_os = "unix", target_os = "macos", target_os = "linux"))]
fn test_filetransfer_scp_find() {
let mut client: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty());
assert!(client
.connect(
String::from("test.rebex.net"),
22,
Some(String::from("demo")),
Some(String::from("password"))
)
.is_ok());
// Check session and scp
assert!(client.session.is_some());
// Search for file (let's search for pop3-*.png); there should be 2
let search_res: Vec<FsEntry> = client.find("pop3-*.png").ok().unwrap();
assert_eq!(search_res.len(), 2);
// verify names
assert_eq!(search_res[0].get_name(), "pop3-browser.png");
assert_eq!(search_res[1].get_name(), "pop3-console-client.png");
// Search directory
let search_res: Vec<FsEntry> = client.find("pub").ok().unwrap();
assert_eq!(search_res.len(), 1);
// Disconnect
assert!(client.disconnect().is_ok());
// Verify err
assert!(client.find("pippo").is_err());
}
#[test]
fn test_filetransfer_scp_recv() {
let mut client: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty());
assert!(client
.connect(
String::from("test.rebex.net"),
22,
Some(String::from("demo")),
Some(String::from("password"))
)
.is_ok());
// Check session and scp
assert!(client.session.is_some());
let file: FsFile = FsFile {
name: String::from("readme.txt"),
abs_path: PathBuf::from("/readme.txt"),
last_change_time: SystemTime::UNIX_EPOCH,
last_access_time: SystemTime::UNIX_EPOCH,
creation_time: SystemTime::UNIX_EPOCH,
size: 0,
ftype: Some(String::from("txt")), // File type
readonly: true,
symlink: None, // UNIX only
user: Some(0), // UNIX only
group: Some(0), // UNIX only
unix_pex: Some((6, 4, 4)), // UNIX only
};
// Receive file
assert!(client.recv_file(&file).is_ok());
// Disconnect
assert!(client.disconnect().is_ok());
}
#[test]
fn test_filetransfer_scp_recv_failed_nosuchfile() {
let mut client: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty());
assert!(client
.connect(
String::from("test.rebex.net"),
22,
Some(String::from("demo")),
Some(String::from("password"))
)
.is_ok());
// Check session and scp
assert!(client.session.is_some());
// Receive file
let file: FsFile = FsFile {
name: String::from("omar.txt"),
abs_path: PathBuf::from("/omar.txt"),
last_change_time: SystemTime::UNIX_EPOCH,
last_access_time: SystemTime::UNIX_EPOCH,
creation_time: SystemTime::UNIX_EPOCH,
size: 0,
ftype: Some(String::from("txt")), // File type
readonly: true,
symlink: None, // UNIX only
user: Some(0), // UNIX only
group: Some(0), // UNIX only
unix_pex: Some((6, 4, 4)), // UNIX only
};
assert!(client.recv_file(&file).is_err());
// Disconnect
assert!(client.disconnect().is_ok());
}
// NOTE: other functions doesn't work with this test scp server
/* NOTE: the server doesn't allow you to create directories
#[test]
fn test_filetransfer_scp_mkdir() {
let mut client: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty());
assert!(client.connect(String::from("test.rebex.net"), 22, Some(String::from("demo")), Some(String::from("password"))).is_ok());
let dir: String = String::from("foo");
// Mkdir
assert!(client.mkdir(dir).is_ok());
// cwd
assert!(client.change_dir(PathBuf::from("foo/").as_path()).is_ok());
assert_eq!(client.wrkdir, PathBuf::from("/foo"));
// Disconnect
assert!(client.disconnect().is_ok());
}
*/
#[test] #[test]
fn test_filetransfer_scp_uninitialized() { fn test_filetransfer_scp_uninitialized() {
let file: FsFile = FsFile { let file: FsFile = FsFile {
@ -1282,9 +1320,19 @@ mod tests {
let mut scp: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty()); let mut scp: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty());
assert!(scp.change_dir(Path::new("/tmp")).is_err()); assert!(scp.change_dir(Path::new("/tmp")).is_err());
assert!(scp.disconnect().is_err()); assert!(scp.disconnect().is_err());
assert!(scp.exec("echo 5").is_err());
assert!(scp.list_dir(Path::new("/tmp")).is_err()); assert!(scp.list_dir(Path::new("/tmp")).is_err());
assert!(scp.mkdir(Path::new("/tmp")).is_err()); assert!(scp.mkdir(Path::new("/tmp")).is_err());
assert!(scp.pwd().is_err()); assert!(scp.pwd().is_err());
assert!(scp
.remove(&make_fsentry(PathBuf::from("/nowhere"), false))
.is_err());
assert!(scp
.rename(
&make_fsentry(PathBuf::from("/nowhere"), false),
PathBuf::from("/culonia").as_path()
)
.is_err());
assert!(scp.stat(Path::new("/tmp")).is_err()); assert!(scp.stat(Path::new("/tmp")).is_err());
assert!(scp.recv_file(&file).is_err()); assert!(scp.recv_file(&file).is_err());
assert!(scp.send_file(&file, Path::new("/tmp/omar.txt")).is_err()); assert!(scp.send_file(&file, Path::new("/tmp/omar.txt")).is_err());

View File

@ -466,10 +466,7 @@ impl FileTransfer for SftpFileTransfer {
match self.sftp.as_ref() { match self.sftp.as_ref() {
Some(_) => { Some(_) => {
// Change working directory // Change working directory
self.wrkdir = match self.get_remote_path(dir) { self.wrkdir = self.get_remote_path(dir)?;
Ok(p) => p,
Err(err) => return Err(err),
};
info!("Changed working directory to {}", self.wrkdir.display()); info!("Changed working directory to {}", self.wrkdir.display());
Ok(self.wrkdir.clone()) Ok(self.wrkdir.clone())
} }
@ -532,10 +529,7 @@ impl FileTransfer for SftpFileTransfer {
match self.sftp.as_ref() { match self.sftp.as_ref() {
Some(sftp) => { Some(sftp) => {
// Get path // Get path
let dir: PathBuf = match self.get_remote_path(path) { let dir: PathBuf = self.get_remote_path(path)?;
Ok(p) => p,
Err(err) => return Err(err),
};
info!("Getting file entries in {}", path.display()); info!("Getting file entries in {}", path.display());
// Get files // Get files
match sftp.readdir(dir.as_path()) { match sftp.readdir(dir.as_path()) {
@ -609,10 +603,7 @@ impl FileTransfer for SftpFileTransfer {
// Remove recursively // Remove recursively
debug!("{} is a directory; removing all directory entries", d.name); debug!("{} is a directory; removing all directory entries", d.name);
// Get directory files // Get directory files
let directory_content: Vec<FsEntry> = match self.list_dir(d.abs_path.as_path()) { let directory_content: Vec<FsEntry> = self.list_dir(d.abs_path.as_path())?;
Ok(entries) => entries,
Err(err) => return Err(err),
};
for entry in directory_content.iter() { for entry in directory_content.iter() {
if let Err(err) = self.remove(&entry) { if let Err(err) = self.remove(&entry) {
return Err(err); return Err(err);
@ -666,10 +657,7 @@ impl FileTransfer for SftpFileTransfer {
match self.sftp.as_ref() { match self.sftp.as_ref() {
Some(sftp) => { Some(sftp) => {
// Get path // Get path
let dir: PathBuf = match self.get_remote_path(path) { let dir: PathBuf = self.get_remote_path(path)?;
Ok(p) => p,
Err(err) => return Err(err),
};
info!("Stat file {}", dir.display()); info!("Stat file {}", dir.display());
// Get file // Get file
match sftp.stat(dir.as_path()) { match sftp.stat(dir.as_path()) {
@ -758,10 +746,7 @@ impl FileTransfer for SftpFileTransfer {
)), )),
Some(sftp) => { Some(sftp) => {
// Get remote file name // Get remote file name
let remote_path: PathBuf = match self.get_remote_path(file.abs_path.as_path()) { let remote_path: PathBuf = self.get_remote_path(file.abs_path.as_path())?;
Ok(p) => p,
Err(err) => return Err(err),
};
info!("Receiving file {}", remote_path.display()); info!("Receiving file {}", remote_path.display());
// Open remote file // Open remote file
match sftp.open(remote_path.as_path()) { match sftp.open(remote_path.as_path()) {
@ -800,7 +785,9 @@ impl FileTransfer for SftpFileTransfer {
mod tests { mod tests {
use super::*; use super::*;
use crate::utils::test_helpers::make_fsentry;
#[cfg(feature = "with-containers")]
use crate::utils::test_helpers::{create_sample_file_entry, write_file, write_ssh_key};
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
#[test] #[test]
@ -813,34 +800,188 @@ mod tests {
} }
#[test] #[test]
fn test_filetransfer_sftp_connect() { #[cfg(feature = "with-containers")]
fn test_filetransfer_sftp_server() {
let mut client: SftpFileTransfer = SftpFileTransfer::new(SshKeyStorage::empty()); let mut client: SftpFileTransfer = SftpFileTransfer::new(SshKeyStorage::empty());
assert_eq!(client.is_connected(), false); // Sample file
let (entry, file): (FsFile, tempfile::NamedTempFile) = create_sample_file_entry();
// Connect
assert!(client assert!(client
.connect( .connect(
String::from("test.rebex.net"), String::from("127.0.0.1"),
22, 10022,
Some(String::from("demo")), Some(String::from("sftp")),
Some(String::from("password")) Some(String::from("password"))
) )
.is_ok()); .is_ok());
// Check session and sftp // Check session and sftp
assert!(client.session.is_some()); assert!(client.session.is_some());
assert!(client.sftp.is_some()); assert!(client.sftp.is_some());
assert_eq!(client.wrkdir, PathBuf::from("/")); assert_eq!(client.wrkdir, PathBuf::from("/config"));
assert_eq!(client.is_connected(), true); assert_eq!(client.is_connected(), true);
// Pwd
assert_eq!(client.wrkdir.clone(), client.pwd().ok().unwrap());
// Stat
let stat: FsFile = client
.stat(PathBuf::from("/config/sshd.pid").as_path())
.ok()
.unwrap()
.unwrap_file();
assert_eq!(stat.name.as_str(), "sshd.pid");
let stat: FsDirectory = client
.stat(PathBuf::from("/config").as_path())
.ok()
.unwrap()
.unwrap_dir();
assert_eq!(stat.name.as_str(), "config");
// Stat (err)
assert!(client
.stat(PathBuf::from("/config/5t0ca220.log").as_path())
.is_err());
// List dir (dir has 4 (one is hidden :D) entries)
assert_eq!(client.list_dir(&Path::new("/config")).unwrap().len(), 4);
// Make directory
assert!(client.mkdir(PathBuf::from("/tmp/omar").as_path()).is_ok());
// Make directory (err)
assert!(client
.mkdir(PathBuf::from("/root/aaaaa/pommlar").as_path())
.is_err());
// Change directory
assert!(client
.change_dir(PathBuf::from("/tmp/omar").as_path())
.is_ok());
// Change directory (err)
assert!(client
.change_dir(PathBuf::from("/tmp/oooo/aaaa/eee").as_path())
.is_err());
// Copy (not supported)
assert!(client
.copy(&FsEntry::File(entry.clone()), PathBuf::from("/").as_path())
.is_err());
// Exec
assert_eq!(client.exec("echo 5").ok().unwrap().as_str(), "5\n");
// Upload 2 files
let mut writable = client
.send_file(&entry, PathBuf::from("omar.txt").as_path())
.ok()
.unwrap();
write_file(&file, &mut writable);
assert!(client.on_sent(writable).is_ok());
let mut writable = client
.send_file(&entry, PathBuf::from("README.md").as_path())
.ok()
.unwrap();
write_file(&file, &mut writable);
assert!(client.on_sent(writable).is_ok());
// Upload file (err)
assert!(client
.send_file(&entry, PathBuf::from("/ommlar/omarone").as_path())
.is_err());
// List dir
let list: Vec<FsEntry> = client
.list_dir(PathBuf::from("/tmp/omar").as_path())
.ok()
.unwrap();
assert_eq!(list.len(), 2);
// Find
assert_eq!(client.find("*.txt").ok().unwrap().len(), 1);
assert_eq!(client.find("*.md").ok().unwrap().len(), 1);
assert_eq!(client.find("*.jpeg").ok().unwrap().len(), 0);
// Rename
assert!(client
.mkdir(PathBuf::from("/tmp/uploads").as_path())
.is_ok());
assert!(client
.rename(
list.get(0).unwrap(),
PathBuf::from("/tmp/uploads/README.txt").as_path()
)
.is_ok());
// Rename (err)
assert!(client
.rename(list.get(0).unwrap(), PathBuf::from("OMARONE").as_path())
.is_err());
let dummy: FsEntry = FsEntry::File(FsFile {
name: String::from("cucumber.txt"),
abs_path: PathBuf::from("/cucumber.txt"),
last_change_time: SystemTime::UNIX_EPOCH,
last_access_time: SystemTime::UNIX_EPOCH,
creation_time: SystemTime::UNIX_EPOCH,
size: 0,
ftype: Some(String::from("txt")), // File type
readonly: true,
symlink: None, // UNIX only
user: Some(0), // UNIX only
group: Some(0), // UNIX only
unix_pex: Some((6, 4, 4)), // UNIX only
});
assert!(client
.rename(&dummy, PathBuf::from("/a/b/c").as_path())
.is_err());
// Remove
assert!(client.remove(list.get(1).unwrap()).is_ok());
assert!(client.remove(list.get(1).unwrap()).is_err());
// Receive file
let mut writable = client
.send_file(&entry, PathBuf::from("/tmp/uploads/README.txt").as_path())
.ok()
.unwrap();
write_file(&file, &mut writable);
assert!(client.on_sent(writable).is_ok());
let file: FsFile = client
.list_dir(PathBuf::from("/tmp/uploads").as_path())
.ok()
.unwrap()
.get(0)
.unwrap()
.clone()
.unwrap_file();
let mut readable = client.recv_file(&file).ok().unwrap();
let mut data: Vec<u8> = vec![0; 1024];
assert!(readable.read(&mut data).is_ok());
assert!(client.on_recv(readable).is_ok());
// Receive file (err)
assert!(client.recv_file(&entry).is_err());
// Cleanup
assert!(client.change_dir(PathBuf::from("/").as_path()).is_ok());
assert!(client
.remove(&make_fsentry(PathBuf::from("/tmp/omar"), true))
.is_ok());
assert!(client
.remove(&make_fsentry(PathBuf::from("/tmp/uploads"), true))
.is_ok());
// Disconnect // Disconnect
assert!(client.disconnect().is_ok()); assert!(client.disconnect().is_ok());
assert_eq!(client.is_connected(), false); assert_eq!(client.is_connected(), false);
} }
#[test]
#[cfg(feature = "with-containers")]
fn test_filetransfer_sftp_ssh_storage() {
let mut storage: SshKeyStorage = SshKeyStorage::empty();
let key_file: tempfile::NamedTempFile = write_ssh_key();
storage.add_key("127.0.0.1", "sftp", key_file.path().to_path_buf());
let mut client: SftpFileTransfer = SftpFileTransfer::new(storage);
// Connect
assert!(client
.connect(
String::from("127.0.0.1"),
10022,
Some(String::from("sftp")),
None,
)
.is_ok());
assert_eq!(client.is_connected(), true);
assert!(client.disconnect().is_ok());
}
#[test] #[test]
fn test_filetransfer_sftp_bad_auth() { fn test_filetransfer_sftp_bad_auth() {
let mut client: SftpFileTransfer = SftpFileTransfer::new(SshKeyStorage::empty()); let mut client: SftpFileTransfer = SftpFileTransfer::new(SshKeyStorage::empty());
assert!(client assert!(client
.connect( .connect(
String::from("test.rebex.net"), String::from("127.0.0.1"),
22, 10022,
Some(String::from("demo")), Some(String::from("demo")),
Some(String::from("badpassword")) Some(String::from("badpassword"))
) )
@ -848,13 +989,52 @@ mod tests {
} }
#[test] #[test]
#[cfg(feature = "with-containers")]
fn test_filetransfer_sftp_no_credentials() { fn test_filetransfer_sftp_no_credentials() {
let mut client: SftpFileTransfer = SftpFileTransfer::new(SshKeyStorage::empty()); let mut client: SftpFileTransfer = SftpFileTransfer::new(SshKeyStorage::empty());
assert!(client assert!(client
.connect(String::from("test.rebex.net"), 22, None, None) .connect(String::from("127.0.0.1"), 10022, None, None)
.is_err()); .is_err());
} }
#[test]
#[cfg(feature = "with-containers")]
fn test_filetransfer_sftp_get_remote_path() {
let mut client: SftpFileTransfer = SftpFileTransfer::new(SshKeyStorage::empty());
// Connect
assert!(client
.connect(
String::from("127.0.0.1"),
10022,
Some(String::from("sftp")),
Some(String::from("password"))
)
.is_ok());
// get realpath
assert!(client
.change_dir(PathBuf::from("/config").as_path())
.is_ok());
assert_eq!(
client
.get_remote_path(PathBuf::from("sshd.pid").as_path())
.ok()
.unwrap(),
PathBuf::from("/config/sshd.pid")
);
// No such file
assert!(client
.get_remote_path(PathBuf::from("omarone.txt").as_path())
.is_err());
// Ok abs path
assert_eq!(
client
.get_remote_path(PathBuf::from("/config/sshd.pid").as_path())
.ok()
.unwrap(),
PathBuf::from("/config/sshd.pid")
);
}
#[test] #[test]
fn test_filetransfer_sftp_bad_server() { fn test_filetransfer_sftp_bad_server() {
let mut client: SftpFileTransfer = SftpFileTransfer::new(SshKeyStorage::empty()); let mut client: SftpFileTransfer = SftpFileTransfer::new(SshKeyStorage::empty());
@ -868,302 +1048,6 @@ mod tests {
.is_err()); .is_err());
} }
#[test]
fn test_filetransfer_sftp_pwd() {
let mut client: SftpFileTransfer = SftpFileTransfer::new(SshKeyStorage::empty());
assert!(client
.connect(
String::from("test.rebex.net"),
22,
Some(String::from("demo")),
Some(String::from("password"))
)
.is_ok());
// Check session and sftp
assert!(client.session.is_some());
assert!(client.sftp.is_some());
assert_eq!(client.wrkdir, PathBuf::from("/"));
// Pwd
assert_eq!(client.wrkdir.clone(), client.pwd().ok().unwrap());
// Disconnect
assert!(client.disconnect().is_ok());
}
#[test]
fn test_filetransfer_sftp_cwd() {
let mut client: SftpFileTransfer = SftpFileTransfer::new(SshKeyStorage::empty());
assert!(client
.connect(
String::from("test.rebex.net"),
22,
Some(String::from("demo")),
Some(String::from("password"))
)
.is_ok());
// Check session and sftp
assert!(client.session.is_some());
assert!(client.sftp.is_some());
assert_eq!(client.wrkdir, PathBuf::from("/"));
// Pwd
assert_eq!(client.wrkdir.clone(), client.pwd().ok().unwrap());
// Cwd (relative)
assert!(client.change_dir(PathBuf::from("pub/").as_path()).is_ok());
assert_eq!(client.wrkdir, PathBuf::from("/pub"));
// Cwd (absolute)
assert!(client.change_dir(PathBuf::from("/").as_path()).is_ok());
assert_eq!(client.wrkdir, PathBuf::from("/"));
// Disconnect
assert!(client.disconnect().is_ok());
}
#[test]
fn test_filetransfer_sftp_copy() {
let mut client: SftpFileTransfer = SftpFileTransfer::new(SshKeyStorage::empty());
assert!(client
.connect(
String::from("test.rebex.net"),
22,
Some(String::from("demo")),
Some(String::from("password"))
)
.is_ok());
// Check session and sftp
assert!(client.session.is_some());
assert!(client.sftp.is_some());
assert_eq!(client.wrkdir, PathBuf::from("/"));
// Copy
let file: FsFile = FsFile {
name: String::from("readme.txt"),
abs_path: PathBuf::from("/readme.txt"),
last_change_time: SystemTime::UNIX_EPOCH,
last_access_time: SystemTime::UNIX_EPOCH,
creation_time: SystemTime::UNIX_EPOCH,
size: 0,
ftype: Some(String::from("txt")), // File type
readonly: true,
symlink: None, // UNIX only
user: Some(0), // UNIX only
group: Some(0), // UNIX only
unix_pex: Some((6, 4, 4)), // UNIX only
};
assert!(client
.copy(&FsEntry::File(file), &Path::new("/tmp/dest.txt"))
.is_err());
}
#[test]
fn test_filetransfer_sftp_cwd_error() {
let mut client: SftpFileTransfer = SftpFileTransfer::new(SshKeyStorage::empty());
assert!(client
.connect(
String::from("test.rebex.net"),
22,
Some(String::from("demo")),
Some(String::from("password"))
)
.is_ok());
// Cwd (abs)
assert!(client
.change_dir(PathBuf::from("/omar/gabber").as_path())
.is_err());
// Cwd (rel)
assert!(client
.change_dir(PathBuf::from("gomar/pett").as_path())
.is_err());
// Disconnect
assert!(client.disconnect().is_ok());
assert!(client
.change_dir(PathBuf::from("gomar/pett").as_path())
.is_err());
}
#[test]
fn test_filetransfer_sftp_ls() {
let mut client: SftpFileTransfer = SftpFileTransfer::new(SshKeyStorage::empty());
assert!(client
.connect(
String::from("test.rebex.net"),
22,
Some(String::from("demo")),
Some(String::from("password"))
)
.is_ok());
// Check session and sftp
assert!(client.session.is_some());
assert!(client.sftp.is_some());
assert_eq!(client.wrkdir, PathBuf::from("/"));
// List dir
let pwd: PathBuf = client.pwd().ok().unwrap();
let files: Vec<FsEntry> = client.list_dir(pwd.as_path()).ok().unwrap();
assert_eq!(files.len(), 3); // There are 3 files
// Disconnect
assert!(client.disconnect().is_ok());
// Verify err
assert!(client.list_dir(pwd.as_path()).is_err());
}
#[test]
fn test_filetransfer_sftp_stat() {
let mut client: SftpFileTransfer = SftpFileTransfer::new(SshKeyStorage::empty());
assert!(client
.connect(
String::from("test.rebex.net"),
22,
Some(String::from("demo")),
Some(String::from("password"))
)
.is_ok());
// Check session and sftp
assert!(client.session.is_some());
assert!(client.sftp.is_some());
assert_eq!(client.wrkdir, PathBuf::from("/"));
let file: FsEntry = client
.stat(PathBuf::from("readme.txt").as_path())
.ok()
.unwrap();
if let FsEntry::File(file) = file {
assert_eq!(file.abs_path, PathBuf::from("/readme.txt"));
} else {
panic!("Expected readme.txt to be a file");
}
}
#[test]
fn test_filetransfer_sftp_exec() {
let mut client: SftpFileTransfer = SftpFileTransfer::new(SshKeyStorage::empty());
assert!(client
.connect(
String::from("test.rebex.net"),
22,
Some(String::from("demo")),
Some(String::from("password"))
)
.is_ok());
// Check session and scp
assert!(client.session.is_some());
// Exec
assert_eq!(client.exec("echo 5").ok().unwrap().as_str(), "5\n");
// Disconnect
assert!(client.disconnect().is_ok());
// Verify err
assert!(client.exec("echo 1").is_err());
}
#[test]
fn test_filetransfer_sftp_find() {
let mut client: SftpFileTransfer = SftpFileTransfer::new(SshKeyStorage::empty());
assert!(client
.connect(
String::from("test.rebex.net"),
22,
Some(String::from("demo")),
Some(String::from("password"))
)
.is_ok());
// Check session and scp
assert!(client.session.is_some());
// Search for file (let's search for pop3-*.png); there should be 2
let search_res: Vec<FsEntry> = client.find("pop3-*.png").ok().unwrap();
assert_eq!(search_res.len(), 2);
// verify names
assert_eq!(search_res[0].get_name(), "pop3-browser.png");
assert_eq!(search_res[1].get_name(), "pop3-console-client.png");
// Search directory
let search_res: Vec<FsEntry> = client.find("pub").ok().unwrap();
assert_eq!(search_res.len(), 1);
// Disconnect
assert!(client.disconnect().is_ok());
// Verify err
assert!(client.find("pippo").is_err());
}
#[test]
fn test_filetransfer_sftp_recv() {
let mut client: SftpFileTransfer = SftpFileTransfer::new(SshKeyStorage::empty());
assert!(client
.connect(
String::from("test.rebex.net"),
22,
Some(String::from("demo")),
Some(String::from("password"))
)
.is_ok());
// Check session and sftp
assert!(client.session.is_some());
assert!(client.sftp.is_some());
assert_eq!(client.wrkdir, PathBuf::from("/"));
let file: FsFile = FsFile {
name: String::from("readme.txt"),
abs_path: PathBuf::from("/readme.txt"),
last_change_time: SystemTime::UNIX_EPOCH,
last_access_time: SystemTime::UNIX_EPOCH,
creation_time: SystemTime::UNIX_EPOCH,
size: 0,
ftype: Some(String::from("txt")), // File type
readonly: true,
symlink: None, // UNIX only
user: Some(0), // UNIX only
group: Some(0), // UNIX only
unix_pex: Some((6, 4, 4)), // UNIX only
};
// Receive file
assert!(client.recv_file(&file).is_ok());
// Disconnect
assert!(client.disconnect().is_ok());
}
#[test]
fn test_filetransfer_sftp_recv_failed_nosuchfile() {
let mut client: SftpFileTransfer = SftpFileTransfer::new(SshKeyStorage::empty());
assert!(client
.connect(
String::from("test.rebex.net"),
22,
Some(String::from("demo")),
Some(String::from("password"))
)
.is_ok());
// Check session and sftp
assert!(client.session.is_some());
assert!(client.sftp.is_some());
assert_eq!(client.wrkdir, PathBuf::from("/"));
// Receive file
let file: FsFile = FsFile {
name: String::from("omar.txt"),
abs_path: PathBuf::from("/omar.txt"),
last_change_time: SystemTime::UNIX_EPOCH,
last_access_time: SystemTime::UNIX_EPOCH,
creation_time: SystemTime::UNIX_EPOCH,
size: 0,
ftype: Some(String::from("txt")), // File type
readonly: true,
symlink: None, // UNIX only
user: Some(0), // UNIX only
group: Some(0), // UNIX only
unix_pex: Some((6, 4, 4)), // UNIX only
};
assert!(client.recv_file(&file).is_err());
// Disconnect
assert!(client.disconnect().is_ok());
}
// NOTE: other functions doesn't work with this test SFTP server
/* NOTE: the server doesn't allow you to create directories
#[test]
fn test_filetransfer_sftp_mkdir() {
let mut client: SftpFileTransfer = SftpFileTransfer::new(SshKeyStorage::empty());
assert!(client.connect(String::from("test.rebex.net"), 22, Some(String::from("demo")), Some(String::from("password"))).is_ok());
let dir: String = String::from("foo");
// Mkdir
assert!(client.mkdir(dir).is_ok());
// cwd
assert!(client.change_dir(PathBuf::from("foo/").as_path()).is_ok());
assert_eq!(client.wrkdir, PathBuf::from("/foo"));
// Disconnect
assert!(client.disconnect().is_ok());
}
*/
#[test] #[test]
fn test_filetransfer_sftp_uninitialized() { fn test_filetransfer_sftp_uninitialized() {
let file: FsFile = FsFile { let file: FsFile = FsFile {
@ -1182,10 +1066,26 @@ mod tests {
}; };
let mut sftp: SftpFileTransfer = SftpFileTransfer::new(SshKeyStorage::empty()); let mut sftp: SftpFileTransfer = SftpFileTransfer::new(SshKeyStorage::empty());
assert!(sftp.change_dir(Path::new("/tmp")).is_err()); assert!(sftp.change_dir(Path::new("/tmp")).is_err());
assert!(sftp
.copy(
&make_fsentry(PathBuf::from("/nowhere"), false),
PathBuf::from("/culonia").as_path()
)
.is_err());
assert!(sftp.exec("echo 5").is_err());
assert!(sftp.disconnect().is_err()); assert!(sftp.disconnect().is_err());
assert!(sftp.list_dir(Path::new("/tmp")).is_err()); assert!(sftp.list_dir(Path::new("/tmp")).is_err());
assert!(sftp.mkdir(Path::new("/tmp")).is_err()); assert!(sftp.mkdir(Path::new("/tmp")).is_err());
assert!(sftp.pwd().is_err()); assert!(sftp.pwd().is_err());
assert!(sftp
.remove(&make_fsentry(PathBuf::from("/nowhere"), false))
.is_err());
assert!(sftp
.rename(
&make_fsentry(PathBuf::from("/nowhere"), false),
PathBuf::from("/culonia").as_path()
)
.is_err());
assert!(sftp.stat(Path::new("/tmp")).is_err()); assert!(sftp.stat(Path::new("/tmp")).is_err());
assert!(sftp.recv_file(&file).is_err()); assert!(sftp.recv_file(&file).is_err());
assert!(sftp.send_file(&file, Path::new("/tmp/omar.txt")).is_err()); assert!(sftp.send_file(&file, Path::new("/tmp/omar.txt")).is_err());

View File

@ -226,6 +226,28 @@ impl FsEntry {
}, },
} }
} }
#[cfg(test)]
/// ### unwrap_file
///
/// Unwrap FsEntry as FsFile
pub fn unwrap_file(self) -> FsFile {
match self {
FsEntry::File(file) => file,
_ => panic!("unwrap_file: not a file"),
}
}
#[cfg(test)]
/// ### unwrap_dir
///
/// Unwrap FsEntry as FsDirectory
pub fn unwrap_dir(self) -> FsDirectory {
match self {
FsEntry::Directory(dir) => dir,
_ => panic!("unwrap_dir: not a directory"),
}
}
} }
#[cfg(test)] #[cfg(test)]
@ -262,6 +284,7 @@ mod tests {
assert_eq!(entry.is_dir(), true); assert_eq!(entry.is_dir(), true);
assert_eq!(entry.is_file(), false); assert_eq!(entry.is_file(), false);
assert_eq!(entry.get_unix_pex(), Some((7, 5, 5))); assert_eq!(entry.get_unix_pex(), Some((7, 5, 5)));
assert_eq!(entry.unwrap_dir().abs_path, PathBuf::from("/foo"));
} }
#[test] #[test]
@ -294,6 +317,47 @@ mod tests {
assert_eq!(entry.is_symlink(), false); assert_eq!(entry.is_symlink(), false);
assert_eq!(entry.is_dir(), false); assert_eq!(entry.is_dir(), false);
assert_eq!(entry.is_file(), true); assert_eq!(entry.is_file(), true);
assert_eq!(entry.unwrap_file().abs_path, PathBuf::from("/bar.txt"));
}
#[test]
#[should_panic]
fn test_fs_fsentry_file_unwrap_bad() {
let t_now: SystemTime = SystemTime::now();
let entry: FsEntry = FsEntry::File(FsFile {
name: String::from("bar.txt"),
abs_path: PathBuf::from("/bar.txt"),
last_change_time: t_now,
last_access_time: t_now,
creation_time: t_now,
size: 8192,
readonly: false,
ftype: Some(String::from("txt")),
symlink: None, // UNIX only
user: Some(0), // UNIX only
group: Some(0), // UNIX only
unix_pex: Some((6, 4, 4)), // UNIX only
});
entry.unwrap_dir();
}
#[test]
#[should_panic]
fn test_fs_fsentry_dir_unwrap_bad() {
let t_now: SystemTime = SystemTime::now();
let entry: FsEntry = FsEntry::Directory(FsDirectory {
name: String::from("foo"),
abs_path: PathBuf::from("/foo"),
last_change_time: t_now,
last_access_time: t_now,
creation_time: t_now,
readonly: false,
symlink: None, // UNIX only
user: Some(0), // UNIX only
group: Some(0), // UNIX only
unix_pex: Some((7, 5, 5)), // UNIX only
});
entry.unwrap_file();
} }
#[test] #[test]

View File

@ -773,10 +773,7 @@ impl Localhost {
if filter.matches(dir.name.as_str()) { if filter.matches(dir.name.as_str()) {
drained.push(FsEntry::Directory(dir.clone())); drained.push(FsEntry::Directory(dir.clone()));
} }
match self.iter_search(dir.abs_path.as_path(), filter) { drained.append(&mut self.iter_search(dir.abs_path.as_path(), filter)?);
Ok(mut filtered) => drained.append(&mut filtered),
Err(err) => return Err(err),
}
} }
FsEntry::File(file) => { FsEntry::File(file) => {
if filter.matches(file.name.as_str()) { if filter.matches(file.name.as_str()) {
@ -829,6 +826,9 @@ impl Localhost {
mod tests { mod tests {
use super::*; use super::*;
#[cfg(any(target_os = "unix", target_os = "macos", target_os = "linux"))]
use crate::utils::test_helpers::{create_sample_file, make_fsentry};
use crate::utils::test_helpers::{make_dir_at, make_file_at};
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
use std::fs::File; use std::fs::File;
@ -975,6 +975,7 @@ mod tests {
//fs::set_permissions(file.path(), perms)?; //fs::set_permissions(file.path(), perms)?;
assert!(host.open_file_write(file.path()).is_err()); assert!(host.open_file_write(file.path()).is_err());
} }
#[cfg(any(target_os = "unix", target_os = "macos", target_os = "linux"))] #[cfg(any(target_os = "unix", target_os = "macos", target_os = "linux"))]
#[test] #[test]
fn test_host_localhost_symlinks() { fn test_host_localhost_symlinks() {
@ -1038,6 +1039,13 @@ mod tests {
assert!(host assert!(host
.mkdir_ex(PathBuf::from("/tmp/test_dir_123456789").as_path(), true) .mkdir_ex(PathBuf::from("/tmp/test_dir_123456789").as_path(), true)
.is_ok()); .is_ok());
// Fail
assert!(host
.mkdir_ex(
PathBuf::from("/aaaa/oooooo/tmp/test_dir_123456789").as_path(),
true
)
.is_err());
} }
#[test] #[test]
@ -1060,6 +1068,13 @@ mod tests {
let files: Vec<FsEntry> = host.list_dir(); let files: Vec<FsEntry> = host.list_dir();
assert_eq!(files.len(), 1); // There should be 1 file now assert_eq!(files.len(), 1); // There should be 1 file now
assert!(host.remove(files.get(0).unwrap()).is_ok()); assert!(host.remove(files.get(0).unwrap()).is_ok());
// Remove unexisting directory
assert!(host
.remove(&make_fsentry(PathBuf::from("/a/b/c/d"), true))
.is_err());
assert!(host
.remove(&make_fsentry(PathBuf::from("/aaaaaaa"), false))
.is_err());
} }
#[test] #[test]
@ -1073,7 +1088,7 @@ mod tests {
let mut host: Localhost = Localhost::new(PathBuf::from(tmpdir.path())).ok().unwrap(); let mut host: Localhost = Localhost::new(PathBuf::from(tmpdir.path())).ok().unwrap();
let files: Vec<FsEntry> = host.list_dir(); let files: Vec<FsEntry> = host.list_dir();
assert_eq!(files.len(), 1); // There should be 1 file now assert_eq!(files.len(), 1); // There should be 1 file now
assert_eq!(get_filename(files.get(0).unwrap()), String::from("foo.txt")); assert_eq!(files.get(0).unwrap().get_name(), "foo.txt");
// Rename file // Rename file
let dst_path: PathBuf = let dst_path: PathBuf =
PathBuf::from(format!("{}/bar.txt", tmpdir.path().display()).as_str()); PathBuf::from(format!("{}/bar.txt", tmpdir.path().display()).as_str());
@ -1083,7 +1098,7 @@ mod tests {
// There should be still 1 file now, but named bar.txt // There should be still 1 file now, but named bar.txt
let files: Vec<FsEntry> = host.list_dir(); let files: Vec<FsEntry> = host.list_dir();
assert_eq!(files.len(), 1); // There should be 0 files now assert_eq!(files.len(), 1); // There should be 0 files now
assert_eq!(get_filename(files.get(0).unwrap()), String::from("bar.txt")); assert_eq!(files.get(0).unwrap().get_name(), "bar.txt");
// Fail // Fail
let bad_path: PathBuf = PathBuf::from("/asdailsjoidoewojdijow/ashdiuahu"); let bad_path: PathBuf = PathBuf::from("/asdailsjoidoewojdijow/ashdiuahu");
assert!(host assert!(host
@ -1131,6 +1146,13 @@ mod tests {
assert!(host.copy(&file1_entry, file2_path.as_path()).is_ok()); assert!(host.copy(&file1_entry, file2_path.as_path()).is_ok());
// Verify host has two files // Verify host has two files
assert_eq!(host.files.len(), 2); assert_eq!(host.files.len(), 2);
// Fail copy
assert!(host
.copy(
&make_fsentry(PathBuf::from("/a/a7/a/a7a"), false),
PathBuf::from("571k422i").as_path()
)
.is_err());
} }
#[cfg(any(target_os = "unix", target_os = "macos", target_os = "linux"))] #[cfg(any(target_os = "unix", target_os = "macos", target_os = "linux"))]
@ -1232,16 +1254,16 @@ mod tests {
let tmpdir: tempfile::TempDir = tempfile::TempDir::new().unwrap(); let tmpdir: tempfile::TempDir = tempfile::TempDir::new().unwrap();
let dir_path: &Path = tmpdir.path(); let dir_path: &Path = tmpdir.path();
// Make files // Make files
assert!(make_sample_file(dir_path, "pippo.txt").is_ok()); assert!(make_file_at(dir_path, "pippo.txt").is_ok());
assert!(make_sample_file(dir_path, "foo.jpg").is_ok()); assert!(make_file_at(dir_path, "foo.jpg").is_ok());
// Make nested struct // Make nested struct
assert!(make_dir(dir_path, "examples").is_ok()); assert!(make_dir_at(dir_path, "examples").is_ok());
let mut subdir: PathBuf = PathBuf::from(dir_path); let mut subdir: PathBuf = PathBuf::from(dir_path);
subdir.push("examples/"); subdir.push("examples/");
assert!(make_sample_file(subdir.as_path(), "omar.txt").is_ok()); assert!(make_file_at(subdir.as_path(), "omar.txt").is_ok());
assert!(make_sample_file(subdir.as_path(), "errors.txt").is_ok()); assert!(make_file_at(subdir.as_path(), "errors.txt").is_ok());
assert!(make_sample_file(subdir.as_path(), "screenshot.png").is_ok()); assert!(make_file_at(subdir.as_path(), "screenshot.png").is_ok());
assert!(make_sample_file(subdir.as_path(), "examples.csv").is_ok()); assert!(make_file_at(subdir.as_path(), "examples.csv").is_ok());
let host: Localhost = Localhost::new(PathBuf::from(dir_path)).ok().unwrap(); let host: Localhost = Localhost::new(PathBuf::from(dir_path)).ok().unwrap();
// Find txt files // Find txt files
let mut result: Vec<FsEntry> = host.find("*.txt").ok().unwrap(); let mut result: Vec<FsEntry> = host.find("*.txt").ok().unwrap();
@ -1300,50 +1322,4 @@ mod tests {
String::from("File already exists") String::from("File already exists")
); );
} }
/// ### make_sample_file
///
/// Make a file with `name` in the current directory
fn make_sample_file(dir: &Path, filename: &str) -> std::io::Result<()> {
let mut p: PathBuf = PathBuf::from(dir);
p.push(filename);
let mut file: File = File::create(p.as_path())?;
write!(
file,
"Lorem ipsum dolor sit amet, consectetur adipiscing elit.\nMauris ultricies consequat eros,\nnec scelerisque magna imperdiet metus.\n"
)?;
Ok(())
}
/// ### make_dir
///
/// Make a directory in `dir`
fn make_dir(dir: &Path, dirname: &str) -> std::io::Result<()> {
let mut p: PathBuf = PathBuf::from(dir);
p.push(dirname);
std::fs::create_dir(p.as_path())
}
/// ### create_sample_file
///
/// Create a sample file
#[cfg(any(target_os = "unix", target_os = "macos", target_os = "linux"))]
fn create_sample_file() -> tempfile::NamedTempFile {
// Write
let mut tmpfile: tempfile::NamedTempFile = tempfile::NamedTempFile::new().unwrap();
write!(
tmpfile,
"Lorem ipsum dolor sit amet, consectetur adipiscing elit.\nMauris ultricies consequat eros,\nnec scelerisque magna imperdiet metus.\n"
)
.unwrap();
tmpfile
}
#[cfg(any(target_os = "unix", target_os = "macos", target_os = "linux"))]
fn get_filename(entry: &FsEntry) -> String {
match entry {
FsEntry::Directory(d) => d.name.clone(),
FsEntry::File(f) => f.name.clone(),
}
}
} }

View File

@ -427,11 +427,12 @@ mod tests {
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
use std::thread::sleep; use std::thread::sleep;
use std::time::Duration; use std::time::Duration;
use tempfile::TempDir;
#[test] #[test]
fn test_system_bookmarks_new() { fn test_system_bookmarks_new() {
let tmp_dir: tempfile::TempDir = create_tmp_dir(); let tmp_dir: tempfile::TempDir = TempDir::new().ok().unwrap();
let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path()); let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path());
// Initialize a new bookmarks client // Initialize a new bookmarks client
let client: BookmarksClient = let client: BookmarksClient =
@ -454,7 +455,7 @@ mod tests {
) )
.is_err()); .is_err());
let tmp_dir: tempfile::TempDir = create_tmp_dir(); let tmp_dir: tempfile::TempDir = TempDir::new().ok().unwrap();
let (cfg_path, _): (PathBuf, PathBuf) = get_paths(tmp_dir.path()); let (cfg_path, _): (PathBuf, PathBuf) = get_paths(tmp_dir.path());
assert!( assert!(
BookmarksClient::new(cfg_path.as_path(), Path::new("/tmp/efnnu/omar"), 16).is_err() BookmarksClient::new(cfg_path.as_path(), Path::new("/tmp/efnnu/omar"), 16).is_err()
@ -464,7 +465,7 @@ mod tests {
#[test] #[test]
fn test_system_bookmarks_new_from_existing() { fn test_system_bookmarks_new_from_existing() {
let tmp_dir: tempfile::TempDir = create_tmp_dir(); let tmp_dir: tempfile::TempDir = TempDir::new().ok().unwrap();
let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path()); let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path());
// Initialize a new bookmarks client // Initialize a new bookmarks client
let mut client: BookmarksClient = let mut client: BookmarksClient =
@ -510,7 +511,7 @@ mod tests {
#[test] #[test]
fn test_system_bookmarks_manipulate_bookmarks() { fn test_system_bookmarks_manipulate_bookmarks() {
let tmp_dir: tempfile::TempDir = create_tmp_dir(); let tmp_dir: tempfile::TempDir = TempDir::new().ok().unwrap();
let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path()); let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path());
// Initialize a new bookmarks client // Initialize a new bookmarks client
let mut client: BookmarksClient = let mut client: BookmarksClient =
@ -556,7 +557,7 @@ mod tests {
#[should_panic] #[should_panic]
fn test_system_bookmarks_bad_bookmark_name() { fn test_system_bookmarks_bad_bookmark_name() {
let tmp_dir: tempfile::TempDir = create_tmp_dir(); let tmp_dir: tempfile::TempDir = TempDir::new().ok().unwrap();
let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path()); let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path());
// Initialize a new bookmarks client // Initialize a new bookmarks client
let mut client: BookmarksClient = let mut client: BookmarksClient =
@ -575,7 +576,7 @@ mod tests {
#[test] #[test]
fn test_system_bookmarks_manipulate_recents() { fn test_system_bookmarks_manipulate_recents() {
let tmp_dir: tempfile::TempDir = create_tmp_dir(); let tmp_dir: tempfile::TempDir = TempDir::new().ok().unwrap();
let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path()); let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path());
// Initialize a new bookmarks client // Initialize a new bookmarks client
let mut client: BookmarksClient = let mut client: BookmarksClient =
@ -610,7 +611,7 @@ mod tests {
#[test] #[test]
fn test_system_bookmarks_dup_recent() { fn test_system_bookmarks_dup_recent() {
let tmp_dir: tempfile::TempDir = create_tmp_dir(); let tmp_dir: tempfile::TempDir = TempDir::new().ok().unwrap();
let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path()); let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path());
// Initialize a new bookmarks client // Initialize a new bookmarks client
let mut client: BookmarksClient = let mut client: BookmarksClient =
@ -635,7 +636,7 @@ mod tests {
#[test] #[test]
fn test_system_bookmarks_recents_more_than_limit() { fn test_system_bookmarks_recents_more_than_limit() {
let tmp_dir: tempfile::TempDir = create_tmp_dir(); let tmp_dir: tempfile::TempDir = TempDir::new().ok().unwrap();
let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path()); let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path());
// Initialize a new bookmarks client // Initialize a new bookmarks client
let mut client: BookmarksClient = let mut client: BookmarksClient =
@ -681,9 +682,8 @@ mod tests {
#[test] #[test]
#[should_panic] #[should_panic]
fn test_system_bookmarks_add_bookmark_empty() { fn test_system_bookmarks_add_bookmark_empty() {
let tmp_dir: tempfile::TempDir = create_tmp_dir(); let tmp_dir: tempfile::TempDir = TempDir::new().ok().unwrap();
let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path()); let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path());
// Initialize a new bookmarks client // Initialize a new bookmarks client
let mut client: BookmarksClient = let mut client: BookmarksClient =
@ -702,19 +702,10 @@ mod tests {
/// ### get_paths /// ### get_paths
/// ///
/// Get paths for configuration and key for bookmarks /// Get paths for configuration and key for bookmarks
fn get_paths(dir: &Path) -> (PathBuf, PathBuf) { fn get_paths(dir: &Path) -> (PathBuf, PathBuf) {
let k: PathBuf = PathBuf::from(dir); let k: PathBuf = PathBuf::from(dir);
let mut c: PathBuf = k.clone(); let mut c: PathBuf = k.clone();
c.push("bookmarks.toml"); c.push("bookmarks.toml");
(c, k) (c, k)
} }
/// ### create_tmp_dir
///
/// Create temporary directory
fn create_tmp_dir() -> tempfile::TempDir {
tempfile::TempDir::new().ok().unwrap()
}
} }

View File

@ -408,10 +408,11 @@ mod tests {
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
use std::io::Read; use std::io::Read;
use tempfile::TempDir;
#[test] #[test]
fn test_system_config_new() { fn test_system_config_new() {
let tmp_dir: tempfile::TempDir = create_tmp_dir(); let tmp_dir: TempDir = TempDir::new().ok().unwrap();
let (cfg_path, ssh_keys_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path()); let (cfg_path, ssh_keys_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path());
let client: ConfigClient = ConfigClient::new(cfg_path.as_path(), ssh_keys_path.as_path()) let client: ConfigClient = ConfigClient::new(cfg_path.as_path(), ssh_keys_path.as_path())
.ok() .ok()
@ -437,14 +438,14 @@ mod tests {
ConfigClient::new(Path::new("/tmp/oifoif/omar"), Path::new("/tmp/efnnu/omar"),) ConfigClient::new(Path::new("/tmp/oifoif/omar"), Path::new("/tmp/efnnu/omar"),)
.is_err() .is_err()
); );
let tmp_dir: tempfile::TempDir = create_tmp_dir(); let tmp_dir: TempDir = TempDir::new().ok().unwrap();
let (cfg_path, _): (PathBuf, PathBuf) = get_paths(tmp_dir.path()); let (cfg_path, _): (PathBuf, PathBuf) = get_paths(tmp_dir.path());
assert!(ConfigClient::new(cfg_path.as_path(), Path::new("/tmp/efnnu/omar")).is_err()); assert!(ConfigClient::new(cfg_path.as_path(), Path::new("/tmp/efnnu/omar")).is_err());
} }
#[test] #[test]
fn test_system_config_from_existing() { fn test_system_config_from_existing() {
let tmp_dir: tempfile::TempDir = create_tmp_dir(); let tmp_dir: TempDir = TempDir::new().ok().unwrap();
let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path()); let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path());
let mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path()) let mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path())
.ok() .ok()
@ -477,7 +478,7 @@ mod tests {
#[test] #[test]
fn test_system_config_text_editor() { fn test_system_config_text_editor() {
let tmp_dir: tempfile::TempDir = create_tmp_dir(); let tmp_dir: TempDir = TempDir::new().ok().unwrap();
let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path()); let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path());
let mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path()) let mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path())
.ok() .ok()
@ -488,7 +489,7 @@ mod tests {
#[test] #[test]
fn test_system_config_default_protocol() { fn test_system_config_default_protocol() {
let tmp_dir: tempfile::TempDir = create_tmp_dir(); let tmp_dir: TempDir = TempDir::new().ok().unwrap();
let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path()); let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path());
let mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path()) let mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path())
.ok() .ok()
@ -502,7 +503,7 @@ mod tests {
#[test] #[test]
fn test_system_config_show_hidden_files() { fn test_system_config_show_hidden_files() {
let tmp_dir: tempfile::TempDir = create_tmp_dir(); let tmp_dir: TempDir = TempDir::new().ok().unwrap();
let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path()); let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path());
let mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path()) let mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path())
.ok() .ok()
@ -513,7 +514,7 @@ mod tests {
#[test] #[test]
fn test_system_config_check_for_updates() { fn test_system_config_check_for_updates() {
let tmp_dir: tempfile::TempDir = create_tmp_dir(); let tmp_dir: TempDir = TempDir::new().ok().unwrap();
let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path()); let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path());
let mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path()) let mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path())
.ok() .ok()
@ -527,7 +528,7 @@ mod tests {
#[test] #[test]
fn test_system_config_group_dirs() { fn test_system_config_group_dirs() {
let tmp_dir: tempfile::TempDir = create_tmp_dir(); let tmp_dir: TempDir = TempDir::new().ok().unwrap();
let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path()); let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path());
let mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path()) let mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path())
.ok() .ok()
@ -540,7 +541,7 @@ mod tests {
#[test] #[test]
fn test_system_config_local_file_fmt() { fn test_system_config_local_file_fmt() {
let tmp_dir: tempfile::TempDir = create_tmp_dir(); let tmp_dir: TempDir = TempDir::new().ok().unwrap();
let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path()); let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path());
let mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path()) let mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path())
.ok() .ok()
@ -555,7 +556,7 @@ mod tests {
#[test] #[test]
fn test_system_config_remote_file_fmt() { fn test_system_config_remote_file_fmt() {
let tmp_dir: tempfile::TempDir = create_tmp_dir(); let tmp_dir: TempDir = TempDir::new().ok().unwrap();
let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path()); let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path());
let mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path()) let mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path())
.ok() .ok()
@ -573,7 +574,7 @@ mod tests {
#[test] #[test]
fn test_system_config_ssh_keys() { fn test_system_config_ssh_keys() {
let tmp_dir: tempfile::TempDir = create_tmp_dir(); let tmp_dir: TempDir = TempDir::new().ok().unwrap();
let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path()); let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path());
let mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path()) let mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path())
.ok() .ok()
@ -637,13 +638,6 @@ mod tests {
(c, k) (c, k)
} }
/// ### create_tmp_dir
///
/// Create temporary directory
fn create_tmp_dir() -> tempfile::TempDir {
tempfile::TempDir::new().ok().unwrap()
}
fn get_sample_rsa_key() -> String { fn get_sample_rsa_key() -> String {
format!( format!(
"-----BEGIN OPENSSH PRIVATE KEY-----\n{}\n-----END OPENSSH PRIVATE KEY-----", "-----BEGIN OPENSSH PRIVATE KEY-----\n{}\n-----END OPENSSH PRIVATE KEY-----",

View File

@ -87,6 +87,16 @@ impl SshKeyStorage {
fn make_mapkey(host: &str, username: &str) -> String { fn make_mapkey(host: &str, username: &str) -> String {
format!("{}@{}", username, host) format!("{}@{}", username, host)
} }
#[cfg(test)]
/// ### add_key
///
/// Add a key to storage
/// NOTE: available only for tests
pub fn add_key(&mut self, host: &str, username: &str, p: PathBuf) {
let key: String = Self::make_mapkey(host, username);
self.hosts.insert(key, p);
}
} }
#[cfg(test)] #[cfg(test)]
@ -100,7 +110,7 @@ mod tests {
#[test] #[test]
fn test_system_sshkey_storage_new() { fn test_system_sshkey_storage_new() {
let tmp_dir: tempfile::TempDir = create_tmp_dir(); let tmp_dir: tempfile::TempDir = tempfile::TempDir::new().ok().unwrap();
let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path()); let (cfg_path, key_path): (PathBuf, PathBuf) = get_paths(tmp_dir.path());
let mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path()) let mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path())
.ok() .ok()
@ -128,6 +138,16 @@ mod tests {
assert_eq!(storage.hosts.len(), 0); assert_eq!(storage.hosts.len(), 0);
} }
#[test]
fn test_system_sshkey_storage_add() {
let mut storage: SshKeyStorage = SshKeyStorage::empty();
storage.add_key("deskichup", "veeso", PathBuf::from("/tmp/omar"));
assert_eq!(
*storage.resolve("deskichup", "veeso").unwrap(),
PathBuf::from("/tmp/omar")
);
}
/// ### get_paths /// ### get_paths
/// ///
/// Get paths for configuration and keys directory /// Get paths for configuration and keys directory
@ -138,11 +158,4 @@ mod tests {
c.push("config.toml"); c.push("config.toml");
(c, k) (c, k)
} }
/// ### create_tmp_dir
///
/// Create temporary directory
fn create_tmp_dir() -> tempfile::TempDir {
tempfile::TempDir::new().ok().unwrap()
}
} }

View File

@ -177,7 +177,7 @@ mod tests {
} }
#[test] #[test]
#[cfg(not(feature = "githubActions"))] #[cfg(not(feature = "github-actions"))]
fn test_ui_context() { fn test_ui_context() {
// Prepare stuff // Prepare stuff
let mut ctx: Context = Context::new(None, Some(String::from("alles kaput"))); let mut ctx: Context = Context::new(None, Some(String::from("alles kaput")));

View File

@ -80,7 +80,7 @@ mod tests {
use super::*; use super::*;
#[test] #[test]
#[cfg(not(all(target_os = "macos", feature = "githubActions")))] #[cfg(not(all(target_os = "macos", feature = "github-actions")))]
fn test_utils_git_check_for_updates() { fn test_utils_git_check_for_updates() {
assert!(check_for_updates("100.0.0").ok().unwrap().is_none()); assert!(check_for_updates("100.0.0").ok().unwrap().is_none());
assert!(check_for_updates("0.0.1").ok().unwrap().is_some()); assert!(check_for_updates("0.0.1").ok().unwrap().is_some());

View File

@ -33,3 +33,7 @@ pub mod git;
pub mod parser; pub mod parser;
pub mod random; pub mod random;
pub mod ui; pub mod ui;
#[cfg(test)]
#[allow(dead_code)]
pub mod test_helpers;

View File

@ -186,13 +186,10 @@ pub fn parse_lstime(tm: &str, fmt_year: &str, fmt_hours: &str) -> Result<SystemT
let this_year: i32 = Utc::now().year(); let this_year: i32 = Utc::now().year();
let date_time_str: String = format!("{} {}", tm, this_year); let date_time_str: String = format!("{} {}", tm, this_year);
// Now parse // Now parse
match NaiveDateTime::parse_from_str( NaiveDateTime::parse_from_str(
date_time_str.as_ref(), date_time_str.as_ref(),
format!("{} %Y", fmt_hours).as_ref(), format!("{} %Y", fmt_hours).as_ref(),
) { )?
Ok(dt) => dt,
Err(err) => return Err(err),
}
} }
}; };
// Convert datetime to system time // Convert datetime to system time

248
src/utils/test_helpers.rs Normal file
View File

@ -0,0 +1,248 @@
//! ## TestHelpers
//!
//! contains helper functions for tests
/**
* MIT License
*
* termscp - Copyright (c) 2021 Christian Visintin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use crate::fs::{FsDirectory, FsEntry, FsFile};
// ext
use std::fs::File;
#[cfg(feature = "with-containers")]
use std::fs::OpenOptions;
#[cfg(feature = "with-containers")]
use std::io::Read;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::time::SystemTime;
use tempfile::NamedTempFile;
pub fn create_sample_file_entry() -> (FsFile, NamedTempFile) {
// Write
let tmpfile = create_sample_file();
(
FsFile {
name: tmpfile
.path()
.file_name()
.unwrap()
.to_string_lossy()
.to_string(),
abs_path: tmpfile.path().to_path_buf(),
last_change_time: SystemTime::UNIX_EPOCH,
last_access_time: SystemTime::UNIX_EPOCH,
creation_time: SystemTime::UNIX_EPOCH,
size: 127,
ftype: None, // File type
readonly: false,
symlink: None, // UNIX only
user: Some(0), // UNIX only
group: Some(0), // UNIX only
unix_pex: Some((6, 4, 4)), // UNIX only
},
tmpfile,
)
}
pub fn create_sample_file() -> NamedTempFile {
// Write
let mut tmpfile: tempfile::NamedTempFile = tempfile::NamedTempFile::new().unwrap();
writeln!(
tmpfile,
"Lorem ipsum dolor sit amet, consectetur adipiscing elit.Mauris ultricies consequat eros,nec scelerisque magna imperdiet metus."
)
.unwrap();
tmpfile
}
/// ### make_file_at
///
/// Make a file with `name` at specified path
pub fn make_file_at(dir: &Path, filename: &str) -> std::io::Result<()> {
let mut p: PathBuf = PathBuf::from(dir);
p.push(filename);
let mut file: File = File::create(p.as_path())?;
writeln!(
file,
"Lorem ipsum dolor sit amet, consectetur adipiscing elit.Mauris ultricies consequat eros,nec scelerisque magna imperdiet metus."
)?;
Ok(())
}
/// ### make_dir_at
///
/// Make a directory in `dir`
pub fn make_dir_at(dir: &Path, dirname: &str) -> std::io::Result<()> {
let mut p: PathBuf = PathBuf::from(dir);
p.push(dirname);
std::fs::create_dir(p.as_path())
}
#[cfg(feature = "with-containers")]
pub fn write_file(file: &NamedTempFile, writable: &mut Box<dyn Write>) {
let mut fhnd = OpenOptions::new()
.create(false)
.read(true)
.write(false)
.open(file.path())
.ok()
.unwrap();
// Read file
let mut buffer: [u8; 65536] = [0; 65536];
assert!(fhnd.read(&mut buffer).is_ok());
// Write file
assert!(writable.write(&buffer).is_ok());
}
#[cfg(feature = "with-containers")]
pub fn write_ssh_key() -> NamedTempFile {
let mut tmpfile: NamedTempFile = NamedTempFile::new().unwrap();
writeln!(
tmpfile,
r"-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn
NhAAAAAwEAAQAAAQEAxKyYUMRCNPlb4ZV1VMofrzApu2l3wgP4Ot9wBvHsw/+RMpcHIbQK
9iQqAVp8Z+M1fJyPXTKjoJtIzuCLF6Sjo0KI7/tFTh+yPnA5QYNLZOIRZb8skumL4gwHww
5Z942FDPuUDQ30C2mZR9lr3Cd5pA8S1ZSPTAV9QQHkpgoS8cAL8QC6dp3CJjUC8wzvXh3I
oN3bTKxCpM10KMEVuWO3lM4Nvr71auB9gzo1sFJ3bwebCZIRH01FROyA/GXRiaOtJFG/9N
nWWI/iG5AJzArKpLZNHIP+FxV/NoRH0WBXm9Wq5MrBYrD1NQzm+kInpS/2sXk3m1aZWqLm
HF2NKRXSbQAAA8iI+KSniPikpwAAAAdzc2gtcnNhAAABAQDErJhQxEI0+VvhlXVUyh+vMC
m7aXfCA/g633AG8ezD/5EylwchtAr2JCoBWnxn4zV8nI9dMqOgm0jO4IsXpKOjQojv+0VO
H7I+cDlBg0tk4hFlvyyS6YviDAfDDln3jYUM+5QNDfQLaZlH2WvcJ3mkDxLVlI9MBX1BAe
SmChLxwAvxALp2ncImNQLzDO9eHcig3dtMrEKkzXQowRW5Y7eUzg2+vvVq4H2DOjWwUndv
B5sJkhEfTUVE7ID8ZdGJo60kUb/02dZYj+IbkAnMCsqktk0cg/4XFX82hEfRYFeb1arkys
FisPU1DOb6QielL/axeTebVplaouYcXY0pFdJtAAAAAwEAAQAAAP8u3PFuTVV5SfGazwIm
MgNaux82iOsAT/HWFWecQAkqqrruUw5f+YajH/riV61NE9aq2qNOkcJrgpTWtqpt980GGd
SHWlgpRWQzfIooEiDk6Pk8RVFZsEykkDlJQSIu2onZjhi5A5ojHgZoGGabDsztSqoyOjPq
6WPvGYRiDAR3leBMyp1WufBCJqAsC4L8CjPJSmnZhc5a0zXkC9Syz74Fa08tdM7bGhtvP1
GmzuYxkgxHH2IFeoumUSBHRiTZayGuRUDel6jgEiUMxenaDKXe7FpYzMm9tQZA10Mm4LhK
5rP9nd2/KRTFRnfZMnKvtIRC9vtlSLBe14qw+4ZCl60AAACAf1kghlO3+HIWplOmk/lCL0
w75Zz+RdvueL9UuoyNN1QrUEY420LsixgWSeRPby+Rb/hW+XSAZJQHowQ8acFJhU85So7f
4O4wcDuE4f6hpsW9tTfkCEUdLCQJ7EKLCrod6jIV7hvI6rvXiVucRpeAzdOaq4uzj2cwDd
tOdYVsnmQAAACBAOVxBsvO/Sr3rZUbNtA6KewZh/09HNGoKNaCeiD7vaSn2UJbbPRByF/o
Oo5zv8ee8r3882NnmG808XfSn7pPZAzbbTmOaJt0fmyZhivCghSNzV6njW3o0PdnC0fGZQ
ruVXgkd7RJFbsIiD4dDcF4VCjwWHfTK21EOgJUA5pN6TNvAAAAgQDbcJWRx8Uyhkj2+srb
3n2Rt6CR7kEl9cw17ItFjMn+pO81/5U2aGw0iLlX7E06TAMQC+dyW/WaxQRey8RRdtbJ1e
TNKCN34QCWkyuYRHGhcNc0quEDayPw5QWGXlP4BzjfRUcPxY9cCXLe5wDLYsX33HwOAc59
RorU9FCmS/654wAAABFyb290QDhjNTBmZDRjMzQ1YQECAw==
-----END OPENSSH PRIVATE KEY-----"
)
.unwrap();
tmpfile
}
/// ### make_fsentry
///
/// Create a FsEntry at specified path
pub fn make_fsentry(path: PathBuf, is_dir: bool) -> FsEntry {
match is_dir {
true => FsEntry::Directory(FsDirectory {
name: path.file_name().unwrap().to_string_lossy().to_string(),
abs_path: path,
last_change_time: SystemTime::UNIX_EPOCH,
last_access_time: SystemTime::UNIX_EPOCH,
creation_time: SystemTime::UNIX_EPOCH,
readonly: false,
symlink: None, // UNIX only
user: Some(0), // UNIX only
group: Some(0), // UNIX only
unix_pex: Some((6, 4, 4)), // UNIX only
}),
false => FsEntry::File(FsFile {
name: path.file_name().unwrap().to_string_lossy().to_string(),
abs_path: path,
last_change_time: SystemTime::UNIX_EPOCH,
last_access_time: SystemTime::UNIX_EPOCH,
creation_time: SystemTime::UNIX_EPOCH,
size: 127,
ftype: None, // File type
readonly: false,
symlink: None, // UNIX only
user: Some(0), // UNIX only
group: Some(0), // UNIX only
unix_pex: Some((6, 4, 4)), // UNIX only
}),
}
}
mod test {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn test_utils_test_helpers_sample_file() {
let (file, _) = create_sample_file_entry();
assert_eq!(file.readonly, false);
}
#[test]
#[cfg(feature = "with-containers")]
fn test_utils_test_helpers_write_file() {
let (_, temp) = create_sample_file_entry();
let tempdest = NamedTempFile::new().unwrap();
let mut dest: Box<dyn Write> = Box::new(
OpenOptions::new()
.create(true)
.read(false)
.write(true)
.open(tempdest.path())
.ok()
.unwrap(),
);
write_file(&temp, &mut dest);
}
#[test]
#[cfg(feature = "with-containers")]
fn test_utils_test_helpers_write_ssh_key() {
let _ = write_ssh_key();
}
#[test]
fn test_utils_test_helpers_make_fsentry() {
assert_eq!(
make_fsentry(PathBuf::from("/tmp/omar.txt"), false)
.unwrap_file()
.name
.as_str(),
"omar.txt"
);
assert_eq!(
make_fsentry(PathBuf::from("/tmp/cards"), true)
.unwrap_dir()
.name
.as_str(),
"cards"
);
}
#[test]
fn test_utils_test_helpers_make_samples() {
let tmpdir: tempfile::TempDir = tempfile::TempDir::new().unwrap();
assert!(make_file_at(tmpdir.path(), "omaroni.txt").is_ok());
assert!(make_file_at(PathBuf::from("/aaaaa/bbbbb/cccc").as_path(), "readme.txt").is_err());
assert!(make_dir_at(tmpdir.path(), "docs").is_ok());
assert!(make_dir_at(PathBuf::from("/aaaaa/bbbbb/cccc").as_path(), "docs").is_err());
}
}

35
tests/docker-compose.yml Normal file
View File

@ -0,0 +1,35 @@
version: "3"
services:
openssh-server:
image: ghcr.io/linuxserver/openssh-server
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/London
- SUDO_ACCESS=false
- PUBLIC_KEY=ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDErJhQxEI0+VvhlXVUyh+vMCm7aXfCA/g633AG8ezD/5EylwchtAr2JCoBWnxn4zV8nI9dMqOgm0jO4IsXpKOjQojv+0VOH7I+cDlBg0tk4hFlvyyS6YviDAfDDln3jYUM+5QNDfQLaZlH2WvcJ3mkDxLVlI9MBX1BAeSmChLxwAvxALp2ncImNQLzDO9eHcig3dtMrEKkzXQowRW5Y7eUzg2+vvVq4H2DOjWwUndvB5sJkhEfTUVE7ID8ZdGJo60kUb/02dZYj+IbkAnMCsqktk0cg/4XFX82hEfRYFeb1arkysFisPU1DOb6QielL/axeTebVplaouYcXY0pFdJt root@8c50fd4c345a
- PASSWORD_ACCESS=true
- USER_PASSWORD=password
- USER_NAME=sftp
ports:
- "10022:2222"
openssh-server-scp:
image: ghcr.io/linuxserver/openssh-server
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/London
- SUDO_ACCESS=false
- PASSWORD_ACCESS=true
- PUBLIC_KEY=ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDErJhQxEI0+VvhlXVUyh+vMCm7aXfCA/g633AG8ezD/5EylwchtAr2JCoBWnxn4zV8nI9dMqOgm0jO4IsXpKOjQojv+0VOH7I+cDlBg0tk4hFlvyyS6YviDAfDDln3jYUM+5QNDfQLaZlH2WvcJ3mkDxLVlI9MBX1BAeSmChLxwAvxALp2ncImNQLzDO9eHcig3dtMrEKkzXQowRW5Y7eUzg2+vvVq4H2DOjWwUndvB5sJkhEfTUVE7ID8ZdGJo60kUb/02dZYj+IbkAnMCsqktk0cg/4XFX82hEfRYFeb1arkysFisPU1DOb6QielL/axeTebVplaouYcXY0pFdJt root@8c50fd4c345a
- USER_PASSWORD=password
- USER_NAME=sftp
ports:
- "10222:2222"
ftp-server:
image: afharo/pure-ftp
ports:
- "10021:21"
- "30000-30009:30000-30009"
environment:
- PUBLICHOST=localhost

29
tests/test.sh Executable file
View File

@ -0,0 +1,29 @@
#!/usr/bin/env sh
if [ ! -f docker-compose.yml ]; then
set -e
cd tests/
set +e
fi
echo "Prepare volume..."
rm -rf /tmp/termscp-test-ftp
mkdir -p /tmp/termscp-test-ftp
echo "Building docker image..."
docker compose build
set -e
docker compose up -d
set +e
# Go back to src root
cd ..
# Run tests
echo "Running tests"
cargo test --features with-containers -- --test-threads 1
TEST_RESULT=$?
# Stop container
cd tests/
echo "Stopping container..."
docker compose stop
exit $TEST_RESULT