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]
@ -6,22 +6,23 @@ env:
CARGO_TERM_COLOR: always
jobs:
coverage:
name: Generate coverage
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Setup rust toolchain
- uses: actions/checkout@v2
- name: Setup containers
run: docker-compose -f "tests/docker-compose.yml" up -d --build
- name: Setup nightly toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: nightly
override: true
- name: Run tests
- name: Run tests (nightly)
uses: actions-rs/cargo@v1
with:
command: test
args: --all-features --no-fail-fast
args: --all-features --lib --no-fail-fast
env:
CARGO_INCREMENTAL: "0"
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:
- uses: actions/checkout@v2
- name: Setup containers
run: docker-compose -f "tests/docker-compose.yml" up -d --build
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true
components: rustfmt, clippy
- uses: actions-rs/cargo@v1
- name: Run tests
uses: actions-rs/cargo@v1
with:
command: test
args: --all-features --no-fail-fast
args: --all-features --lib --no-fail-fast
- name: Format
run: cargo fmt --all -- --check
- name: Clippy

View File

@ -14,6 +14,6 @@ jobs:
- name: Build
run: cargo build --verbose
- 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
run: cargo clippy

View File

@ -14,6 +14,6 @@ jobs:
- name: Build
run: cargo build --verbose
- 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
run: cargo clippy

View File

@ -22,6 +22,12 @@
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:
- 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

View File

@ -59,7 +59,8 @@ wildmatch = "2.0.0"
pretty_assertions = "0.7.2"
[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\"))".dependencies]

View File

@ -3,15 +3,26 @@
Document audience: developers
- [Developer Manual](#developer-manual)
- [How to test](#how-to-test)
- [How termscp works](#how-termscp-works)
- [Activities](#activities)
- [The Context](#the-context)
- [Tests fails due to receivers](#tests-fails-due-to-receivers)
- [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>
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
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
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}")),
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 {
user_interface: ui,
remote: remote,

View File

@ -208,6 +208,25 @@ mod tests {
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 {
// Write
let mut tmpfile: tempfile::NamedTempFile = tempfile::NamedTempFile::new().unwrap();

View File

@ -491,7 +491,10 @@ impl FileTransfer for FtpFileTransfer {
info!("Disconnecting from FTP server...");
match &mut self.stream {
Some(stream) => match stream.quit() {
Ok(_) => Ok(()),
Ok(_) => {
self.stream = None;
Ok(())
}
Err(err) => Err(FileTransferError::new_ex(
FileTransferErrorType::ConnectionError,
err.to_string(),
@ -859,9 +862,14 @@ impl FileTransfer for FtpFileTransfer {
mod tests {
use super::*;
use crate::utils::file::open_file;
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 std::io::{Read, Write};
use std::time::Duration;
#[test]
@ -875,120 +883,281 @@ mod tests {
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]
fn test_filetransfer_ftp_parse_list_line_unix() {
let mut ftp: FtpFileTransfer = FtpFileTransfer::new(false);
// Simple file
let fs_entry: FsEntry = ftp
let file: FsFile = ftp
.parse_list_line(
PathBuf::from("/tmp").as_path(),
"-rw-rw-r-- 1 root dialout 8192 Nov 5 2018 omar.txt",
)
.ok()
.unwrap();
if let FsEntry::File(file) = fs_entry {
assert_eq!(file.abs_path, PathBuf::from("/tmp/omar.txt"));
assert_eq!(file.name, String::from("omar.txt"));
assert_eq!(file.size, 8192);
assert!(file.symlink.is_none());
assert_eq!(file.user, None);
assert_eq!(file.group, None);
assert_eq!(file.unix_pex.unwrap(), (6, 6, 4));
assert_eq!(
file.last_access_time
.duration_since(SystemTime::UNIX_EPOCH)
.ok()
.unwrap(),
Duration::from_secs(1541376000)
);
assert_eq!(
file.last_change_time
.duration_since(SystemTime::UNIX_EPOCH)
.ok()
.unwrap(),
Duration::from_secs(1541376000)
);
assert_eq!(
file.creation_time
.duration_since(SystemTime::UNIX_EPOCH)
.ok()
.unwrap(),
Duration::from_secs(1541376000)
);
} else {
panic!("Expected file, got directory");
}
.unwrap()
.unwrap_file();
assert_eq!(file.abs_path, PathBuf::from("/tmp/omar.txt"));
assert_eq!(file.name, String::from("omar.txt"));
assert_eq!(file.size, 8192);
assert!(file.symlink.is_none());
assert_eq!(file.user, None);
assert_eq!(file.group, None);
assert_eq!(file.unix_pex.unwrap(), (6, 6, 4));
assert_eq!(
file.last_access_time
.duration_since(SystemTime::UNIX_EPOCH)
.ok()
.unwrap(),
Duration::from_secs(1541376000)
);
assert_eq!(
file.last_change_time
.duration_since(SystemTime::UNIX_EPOCH)
.ok()
.unwrap(),
Duration::from_secs(1541376000)
);
assert_eq!(
file.creation_time
.duration_since(SystemTime::UNIX_EPOCH)
.ok()
.unwrap(),
Duration::from_secs(1541376000)
);
// Simple file with number as gid, uid
let fs_entry: FsEntry = ftp
let file: FsFile = ftp
.parse_list_line(
PathBuf::from("/tmp").as_path(),
"-rwxr-xr-x 1 0 9 4096 Nov 5 16:32 omar.txt",
)
.ok()
.unwrap();
if let FsEntry::File(file) = fs_entry {
assert_eq!(file.abs_path, PathBuf::from("/tmp/omar.txt"));
assert_eq!(file.name, String::from("omar.txt"));
assert_eq!(file.size, 4096);
assert!(file.symlink.is_none());
assert_eq!(file.user, Some(0));
assert_eq!(file.group, Some(9));
assert_eq!(file.unix_pex.unwrap(), (7, 5, 5));
assert_eq!(
fmt_time(file.last_access_time, "%m %d %M").as_str(),
"11 05 32"
);
assert_eq!(
fmt_time(file.last_change_time, "%m %d %M").as_str(),
"11 05 32"
);
assert_eq!(
fmt_time(file.creation_time, "%m %d %M").as_str(),
"11 05 32"
);
} else {
panic!("Expected file, got directory");
}
.unwrap()
.unwrap_file();
assert_eq!(file.abs_path, PathBuf::from("/tmp/omar.txt"));
assert_eq!(file.name, String::from("omar.txt"));
assert_eq!(file.size, 4096);
assert!(file.symlink.is_none());
assert_eq!(file.user, Some(0));
assert_eq!(file.group, Some(9));
assert_eq!(file.unix_pex.unwrap(), (7, 5, 5));
assert_eq!(
fmt_time(file.last_access_time, "%m %d %M").as_str(),
"11 05 32"
);
assert_eq!(
fmt_time(file.last_change_time, "%m %d %M").as_str(),
"11 05 32"
);
assert_eq!(
fmt_time(file.creation_time, "%m %d %M").as_str(),
"11 05 32"
);
// Directory
let fs_entry: FsEntry = ftp
let dir: FsDirectory = ftp
.parse_list_line(
PathBuf::from("/tmp").as_path(),
"drwxrwxr-x 1 0 9 4096 Nov 5 2018 docs",
)
.ok()
.unwrap();
if let FsEntry::Directory(dir) = fs_entry {
assert_eq!(dir.abs_path, PathBuf::from("/tmp/docs"));
assert_eq!(dir.name, String::from("docs"));
assert!(dir.symlink.is_none());
assert_eq!(dir.user, Some(0));
assert_eq!(dir.group, Some(9));
assert_eq!(dir.unix_pex.unwrap(), (7, 7, 5));
assert_eq!(
dir.last_access_time
.duration_since(SystemTime::UNIX_EPOCH)
.ok()
.unwrap(),
Duration::from_secs(1541376000)
);
assert_eq!(
dir.last_change_time
.duration_since(SystemTime::UNIX_EPOCH)
.ok()
.unwrap(),
Duration::from_secs(1541376000)
);
assert_eq!(
dir.creation_time
.duration_since(SystemTime::UNIX_EPOCH)
.ok()
.unwrap(),
Duration::from_secs(1541376000)
);
assert_eq!(dir.readonly, false);
} else {
panic!("Expected directory, got directory");
}
.unwrap()
.unwrap_dir();
assert_eq!(dir.abs_path, PathBuf::from("/tmp/docs"));
assert_eq!(dir.name, String::from("docs"));
assert!(dir.symlink.is_none());
assert_eq!(dir.user, Some(0));
assert_eq!(dir.group, Some(9));
assert_eq!(dir.unix_pex.unwrap(), (7, 7, 5));
assert_eq!(
dir.last_access_time
.duration_since(SystemTime::UNIX_EPOCH)
.ok()
.unwrap(),
Duration::from_secs(1541376000)
);
assert_eq!(
dir.last_change_time
.duration_since(SystemTime::UNIX_EPOCH)
.ok()
.unwrap(),
Duration::from_secs(1541376000)
);
assert_eq!(
dir.creation_time
.duration_since(SystemTime::UNIX_EPOCH)
.ok()
.unwrap(),
Duration::from_secs(1541376000)
);
assert_eq!(dir.readonly, false);
// Error
assert!(ftp
.parse_list_line(
@ -1002,186 +1171,85 @@ mod tests {
fn test_filetransfer_ftp_parse_list_line_dos() {
let mut ftp: FtpFileTransfer = FtpFileTransfer::new(false);
// Simple file
let fs_entry: FsEntry = ftp
let file: FsFile = ftp
.parse_list_line(
PathBuf::from("/tmp").as_path(),
"04-08-14 03:09PM 8192 omar.txt",
)
.ok()
.unwrap();
if let FsEntry::File(file) = fs_entry {
assert_eq!(file.abs_path, PathBuf::from("/tmp/omar.txt"));
assert_eq!(file.name, String::from("omar.txt"));
assert_eq!(file.size, 8192);
assert!(file.symlink.is_none());
assert_eq!(file.user, None);
assert_eq!(file.group, None);
assert_eq!(file.unix_pex, None);
assert_eq!(
file.last_access_time
.duration_since(SystemTime::UNIX_EPOCH)
.ok()
.unwrap(),
Duration::from_secs(1407164940)
);
assert_eq!(
file.last_change_time
.duration_since(SystemTime::UNIX_EPOCH)
.ok()
.unwrap(),
Duration::from_secs(1407164940)
);
assert_eq!(
file.creation_time
.duration_since(SystemTime::UNIX_EPOCH)
.ok()
.unwrap(),
Duration::from_secs(1407164940)
);
} else {
panic!("Expected file, got directory");
}
.unwrap()
.unwrap_file();
assert_eq!(file.abs_path, PathBuf::from("/tmp/omar.txt"));
assert_eq!(file.name, String::from("omar.txt"));
assert_eq!(file.size, 8192);
assert!(file.symlink.is_none());
assert_eq!(file.user, None);
assert_eq!(file.group, None);
assert_eq!(file.unix_pex, None);
assert_eq!(
file.last_access_time
.duration_since(SystemTime::UNIX_EPOCH)
.ok()
.unwrap(),
Duration::from_secs(1407164940)
);
assert_eq!(
file.last_change_time
.duration_since(SystemTime::UNIX_EPOCH)
.ok()
.unwrap(),
Duration::from_secs(1407164940)
);
assert_eq!(
file.creation_time
.duration_since(SystemTime::UNIX_EPOCH)
.ok()
.unwrap(),
Duration::from_secs(1407164940)
);
// Directory
let fs_entry: FsEntry = ftp
let dir: FsDirectory = ftp
.parse_list_line(
PathBuf::from("/tmp").as_path(),
"04-08-14 03:09PM <DIR> docs",
)
.ok()
.unwrap();
if let FsEntry::Directory(dir) = fs_entry {
assert_eq!(dir.abs_path, PathBuf::from("/tmp/docs"));
assert_eq!(dir.name, String::from("docs"));
assert!(dir.symlink.is_none());
assert_eq!(dir.user, None);
assert_eq!(dir.group, None);
assert_eq!(dir.unix_pex, None);
assert_eq!(
dir.last_access_time
.duration_since(SystemTime::UNIX_EPOCH)
.ok()
.unwrap(),
Duration::from_secs(1407164940)
);
assert_eq!(
dir.last_change_time
.duration_since(SystemTime::UNIX_EPOCH)
.ok()
.unwrap(),
Duration::from_secs(1407164940)
);
assert_eq!(
dir.creation_time
.duration_since(SystemTime::UNIX_EPOCH)
.ok()
.unwrap(),
Duration::from_secs(1407164940)
);
assert_eq!(dir.readonly, false);
} else {
panic!("Expected directory, got directory");
}
.unwrap()
.unwrap_dir();
assert_eq!(dir.abs_path, PathBuf::from("/tmp/docs"));
assert_eq!(dir.name, String::from("docs"));
assert!(dir.symlink.is_none());
assert_eq!(dir.user, None);
assert_eq!(dir.group, None);
assert_eq!(dir.unix_pex, None);
assert_eq!(
dir.last_access_time
.duration_since(SystemTime::UNIX_EPOCH)
.ok()
.unwrap(),
Duration::from_secs(1407164940)
);
assert_eq!(
dir.last_change_time
.duration_since(SystemTime::UNIX_EPOCH)
.ok()
.unwrap(),
Duration::from_secs(1407164940)
);
assert_eq!(
dir.creation_time
.duration_since(SystemTime::UNIX_EPOCH)
.ok()
.unwrap(),
Duration::from_secs(1407164940)
);
assert_eq!(dir.readonly, false);
// Error
assert!(ftp
.parse_list_line(PathBuf::from("/").as_path(), "04-08-14 omar.txt")
.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]
fn test_filetransfer_ftp_list_dir_dos_syntax() {
let mut ftp: FtpFileTransfer = FtpFileTransfer::new(false);
@ -1205,94 +1273,16 @@ mod tests {
}
#[test]
#[cfg(not(target_os = "macos"))]
fn test_filetransfer_ftp_list_dir_unix_syntax() {
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("/"));
// 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());
fn test_filetransfer_ftp_get_name_and_link() {
let client: FtpFileTransfer = FtpFileTransfer::new(false);
assert_eq!(
client.get_name_and_link("Cargo.toml"),
(String::from("Cargo.toml"), None)
);
assert_eq!(
client.get_name_and_link("Cargo -> Cargo.toml"),
(String::from("Cargo"), Some(PathBuf::from("Cargo.toml")))
);
}
#[test]
@ -1316,9 +1306,25 @@ mod tests {
assert!(ftp.disconnect().is_err());
assert!(ftp.list_dir(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.stat(Path::new("/tmp")).is_err());
assert!(ftp.recv_file(&file).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()) {
drained.push(FsEntry::Directory(dir.clone()));
}
match self.iter_search(dir.abs_path.as_path(), filter) {
Ok(mut filtered) => drained.append(&mut filtered),
Err(err) => return Err(err),
}
drained.append(&mut self.iter_search(dir.abs_path.as_path(), filter)?);
}
FsEntry::File(file) => {
if filter.matches(file.name.as_str()) {

View File

@ -445,10 +445,9 @@ impl FileTransfer for ScpFileTransfer {
self.session = Some(session);
// Get working directory
debug!("Getting working directory...");
match self.perform_shell_cmd("pwd") {
Ok(output) => self.wrkdir = PathBuf::from(output.as_str().trim()),
Err(err) => return Err(err),
}
self.wrkdir = self
.perform_shell_cmd("pwd")
.map(|x| PathBuf::from(x.as_str().trim()))?;
info!(
"Connection established; working directory: {}",
self.wrkdir.display()
@ -486,7 +485,7 @@ impl FileTransfer for ScpFileTransfer {
///
/// Indicates whether the client is connected to remote
fn is_connected(&self) -> bool {
self.session.as_ref().is_some()
self.session.is_some()
}
/// ### pwd
@ -853,7 +852,15 @@ impl FileTransfer for ScpFileTransfer {
) -> Result<Box<dyn Write>, FileTransferError> {
match self.session.as_ref() {
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!(
"Sending file {} to {}",
local.abs_path.display(),
@ -963,8 +970,12 @@ impl FileTransfer for ScpFileTransfer {
mod tests {
use super::*;
use crate::utils::test_helpers::make_fsentry;
use pretty_assertions::assert_eq;
#[cfg(feature = "with-containers")]
use crate::utils::test_helpers::{create_sample_file_entry, write_file, write_ssh_key};
#[test]
fn test_filetransfer_scp_new() {
let client: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty());
@ -973,31 +984,210 @@ mod tests {
}
#[test]
fn test_filetransfer_scp_connect() {
#[cfg(feature = "with-containers")]
fn test_filetransfer_scp_server() {
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
.connect(
String::from("test.rebex.net"),
22,
Some(String::from("demo")),
String::from("127.0.0.1"),
10222,
Some(String::from("sftp")),
Some(String::from("password"))
)
.is_ok());
// Check session and scp
// Check session and sftp
assert!(client.session.is_some());
assert_eq!(client.wrkdir, PathBuf::from("/config"));
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
assert!(client.disconnect().is_ok());
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]
fn test_filetransfer_scp_bad_auth() {
let mut client: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty());
assert!(client
.connect(
String::from("test.rebex.net"),
22,
String::from("127.0.0.1"),
10222,
Some(String::from("demo")),
Some(String::from("badpassword"))
)
@ -1005,10 +1195,11 @@ mod tests {
}
#[test]
#[cfg(feature = "with-containers")]
fn test_filetransfer_scp_no_credentials() {
let mut client: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty());
assert!(client
.connect(String::from("test.rebex.net"), 22, None, None)
.connect(String::from("127.0.0.1"), 10222, None, None)
.is_err());
}
@ -1024,245 +1215,92 @@ mod tests {
)
.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]
#[cfg(any(target_os = "unix", target_os = "macos", target_os = "linux"))]
fn test_filetransfer_scp_cwd() {
fn test_filetransfer_scp_parse_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"))
// File
let entry: FsFile = client
.parse_ls_output(
PathBuf::from("/tmp").as_path(),
"-rw-r--r-- 1 root root 2056 giu 13 21:11 Cargo.toml",
)
.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()
.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");
}
.unwrap()
.unwrap_file();
assert_eq!(entry.name.as_str(), "Cargo.toml");
assert_eq!(entry.abs_path, PathBuf::from("/tmp/Cargo.toml"));
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]
fn test_filetransfer_scp_exec() {
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());
// Exec
assert_eq!(client.exec("echo 5").ok().unwrap().as_str(), "5\n");
// Disconnect
assert!(client.disconnect().is_ok());
fn test_filetransfer_scp_get_name_and_link() {
let client: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty());
assert_eq!(
client.get_name_and_link("Cargo.toml"),
(String::from("Cargo.toml"), None)
);
assert_eq!(
client.get_name_and_link("Cargo -> Cargo.toml"),
(String::from("Cargo"), Some(PathBuf::from("Cargo.toml")))
);
}
#[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]
fn test_filetransfer_scp_uninitialized() {
let file: FsFile = FsFile {
@ -1282,9 +1320,19 @@ mod tests {
let mut scp: ScpFileTransfer = ScpFileTransfer::new(SshKeyStorage::empty());
assert!(scp.change_dir(Path::new("/tmp")).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.mkdir(Path::new("/tmp")).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.recv_file(&file).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() {
Some(_) => {
// Change working directory
self.wrkdir = match self.get_remote_path(dir) {
Ok(p) => p,
Err(err) => return Err(err),
};
self.wrkdir = self.get_remote_path(dir)?;
info!("Changed working directory to {}", self.wrkdir.display());
Ok(self.wrkdir.clone())
}
@ -532,10 +529,7 @@ impl FileTransfer for SftpFileTransfer {
match self.sftp.as_ref() {
Some(sftp) => {
// Get path
let dir: PathBuf = match self.get_remote_path(path) {
Ok(p) => p,
Err(err) => return Err(err),
};
let dir: PathBuf = self.get_remote_path(path)?;
info!("Getting file entries in {}", path.display());
// Get files
match sftp.readdir(dir.as_path()) {
@ -609,10 +603,7 @@ impl FileTransfer for SftpFileTransfer {
// Remove recursively
debug!("{} is a directory; removing all directory entries", d.name);
// Get directory files
let directory_content: Vec<FsEntry> = match self.list_dir(d.abs_path.as_path()) {
Ok(entries) => entries,
Err(err) => return Err(err),
};
let directory_content: Vec<FsEntry> = self.list_dir(d.abs_path.as_path())?;
for entry in directory_content.iter() {
if let Err(err) = self.remove(&entry) {
return Err(err);
@ -666,10 +657,7 @@ impl FileTransfer for SftpFileTransfer {
match self.sftp.as_ref() {
Some(sftp) => {
// Get path
let dir: PathBuf = match self.get_remote_path(path) {
Ok(p) => p,
Err(err) => return Err(err),
};
let dir: PathBuf = self.get_remote_path(path)?;
info!("Stat file {}", dir.display());
// Get file
match sftp.stat(dir.as_path()) {
@ -758,10 +746,7 @@ impl FileTransfer for SftpFileTransfer {
)),
Some(sftp) => {
// Get remote file name
let remote_path: PathBuf = match self.get_remote_path(file.abs_path.as_path()) {
Ok(p) => p,
Err(err) => return Err(err),
};
let remote_path: PathBuf = self.get_remote_path(file.abs_path.as_path())?;
info!("Receiving file {}", remote_path.display());
// Open remote file
match sftp.open(remote_path.as_path()) {
@ -800,7 +785,9 @@ impl FileTransfer for SftpFileTransfer {
mod tests {
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;
#[test]
@ -813,34 +800,188 @@ mod tests {
}
#[test]
fn test_filetransfer_sftp_connect() {
#[cfg(feature = "with-containers")]
fn test_filetransfer_sftp_server() {
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
.connect(
String::from("test.rebex.net"),
22,
Some(String::from("demo")),
String::from("127.0.0.1"),
10022,
Some(String::from("sftp")),
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("/"));
assert_eq!(client.wrkdir, PathBuf::from("/config"));
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
assert!(client.disconnect().is_ok());
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]
fn test_filetransfer_sftp_bad_auth() {
let mut client: SftpFileTransfer = SftpFileTransfer::new(SshKeyStorage::empty());
assert!(client
.connect(
String::from("test.rebex.net"),
22,
String::from("127.0.0.1"),
10022,
Some(String::from("demo")),
Some(String::from("badpassword"))
)
@ -848,13 +989,52 @@ mod tests {
}
#[test]
#[cfg(feature = "with-containers")]
fn test_filetransfer_sftp_no_credentials() {
let mut client: SftpFileTransfer = SftpFileTransfer::new(SshKeyStorage::empty());
assert!(client
.connect(String::from("test.rebex.net"), 22, None, None)
.connect(String::from("127.0.0.1"), 10022, None, None)
.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]
fn test_filetransfer_sftp_bad_server() {
let mut client: SftpFileTransfer = SftpFileTransfer::new(SshKeyStorage::empty());
@ -868,302 +1048,6 @@ mod tests {
.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]
fn test_filetransfer_sftp_uninitialized() {
let file: FsFile = FsFile {
@ -1182,10 +1066,26 @@ mod tests {
};
let mut sftp: SftpFileTransfer = SftpFileTransfer::new(SshKeyStorage::empty());
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.list_dir(Path::new("/tmp")).is_err());
assert!(sftp.mkdir(Path::new("/tmp")).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.recv_file(&file).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)]
@ -262,6 +284,7 @@ mod tests {
assert_eq!(entry.is_dir(), true);
assert_eq!(entry.is_file(), false);
assert_eq!(entry.get_unix_pex(), Some((7, 5, 5)));
assert_eq!(entry.unwrap_dir().abs_path, PathBuf::from("/foo"));
}
#[test]
@ -294,6 +317,47 @@ mod tests {
assert_eq!(entry.is_symlink(), false);
assert_eq!(entry.is_dir(), false);
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]

View File

@ -773,10 +773,7 @@ impl Localhost {
if filter.matches(dir.name.as_str()) {
drained.push(FsEntry::Directory(dir.clone()));
}
match self.iter_search(dir.abs_path.as_path(), filter) {
Ok(mut filtered) => drained.append(&mut filtered),
Err(err) => return Err(err),
}
drained.append(&mut self.iter_search(dir.abs_path.as_path(), filter)?);
}
FsEntry::File(file) => {
if filter.matches(file.name.as_str()) {
@ -829,6 +826,9 @@ impl Localhost {
mod tests {
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 std::fs::File;
@ -975,6 +975,7 @@ mod tests {
//fs::set_permissions(file.path(), perms)?;
assert!(host.open_file_write(file.path()).is_err());
}
#[cfg(any(target_os = "unix", target_os = "macos", target_os = "linux"))]
#[test]
fn test_host_localhost_symlinks() {
@ -1038,6 +1039,13 @@ mod tests {
assert!(host
.mkdir_ex(PathBuf::from("/tmp/test_dir_123456789").as_path(), true)
.is_ok());
// Fail
assert!(host
.mkdir_ex(
PathBuf::from("/aaaa/oooooo/tmp/test_dir_123456789").as_path(),
true
)
.is_err());
}
#[test]
@ -1060,6 +1068,13 @@ mod tests {
let files: Vec<FsEntry> = host.list_dir();
assert_eq!(files.len(), 1); // There should be 1 file now
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]
@ -1073,7 +1088,7 @@ mod tests {
let mut host: Localhost = Localhost::new(PathBuf::from(tmpdir.path())).ok().unwrap();
let files: Vec<FsEntry> = host.list_dir();
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
let dst_path: PathBuf =
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
let files: Vec<FsEntry> = host.list_dir();
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
let bad_path: PathBuf = PathBuf::from("/asdailsjoidoewojdijow/ashdiuahu");
assert!(host
@ -1131,6 +1146,13 @@ mod tests {
assert!(host.copy(&file1_entry, file2_path.as_path()).is_ok());
// Verify host has two files
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"))]
@ -1232,16 +1254,16 @@ mod tests {
let tmpdir: tempfile::TempDir = tempfile::TempDir::new().unwrap();
let dir_path: &Path = tmpdir.path();
// Make files
assert!(make_sample_file(dir_path, "pippo.txt").is_ok());
assert!(make_sample_file(dir_path, "foo.jpg").is_ok());
assert!(make_file_at(dir_path, "pippo.txt").is_ok());
assert!(make_file_at(dir_path, "foo.jpg").is_ok());
// 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);
subdir.push("examples/");
assert!(make_sample_file(subdir.as_path(), "omar.txt").is_ok());
assert!(make_sample_file(subdir.as_path(), "errors.txt").is_ok());
assert!(make_sample_file(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(), "omar.txt").is_ok());
assert!(make_file_at(subdir.as_path(), "errors.txt").is_ok());
assert!(make_file_at(subdir.as_path(), "screenshot.png").is_ok());
assert!(make_file_at(subdir.as_path(), "examples.csv").is_ok());
let host: Localhost = Localhost::new(PathBuf::from(dir_path)).ok().unwrap();
// Find txt files
let mut result: Vec<FsEntry> = host.find("*.txt").ok().unwrap();
@ -1300,50 +1322,4 @@ mod tests {
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 std::thread::sleep;
use std::time::Duration;
use tempfile::TempDir;
#[test]
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());
// Initialize a new bookmarks client
let client: BookmarksClient =
@ -454,7 +455,7 @@ mod tests {
)
.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());
assert!(
BookmarksClient::new(cfg_path.as_path(), Path::new("/tmp/efnnu/omar"), 16).is_err()
@ -464,7 +465,7 @@ mod tests {
#[test]
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());
// Initialize a new bookmarks client
let mut client: BookmarksClient =
@ -510,7 +511,7 @@ mod tests {
#[test]
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());
// Initialize a new bookmarks client
let mut client: BookmarksClient =
@ -556,7 +557,7 @@ mod tests {
#[should_panic]
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());
// Initialize a new bookmarks client
let mut client: BookmarksClient =
@ -575,7 +576,7 @@ mod tests {
#[test]
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());
// Initialize a new bookmarks client
let mut client: BookmarksClient =
@ -610,7 +611,7 @@ mod tests {
#[test]
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());
// Initialize a new bookmarks client
let mut client: BookmarksClient =
@ -635,7 +636,7 @@ mod tests {
#[test]
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());
// Initialize a new bookmarks client
let mut client: BookmarksClient =
@ -681,9 +682,8 @@ mod tests {
#[test]
#[should_panic]
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());
// Initialize a new bookmarks client
let mut client: BookmarksClient =
@ -702,19 +702,10 @@ mod tests {
/// ### get_paths
///
/// Get paths for configuration and key for bookmarks
fn get_paths(dir: &Path) -> (PathBuf, PathBuf) {
let k: PathBuf = PathBuf::from(dir);
let mut c: PathBuf = k.clone();
c.push("bookmarks.toml");
(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 std::io::Read;
use tempfile::TempDir;
#[test]
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 client: ConfigClient = ConfigClient::new(cfg_path.as_path(), ssh_keys_path.as_path())
.ok()
@ -437,14 +438,14 @@ mod tests {
ConfigClient::new(Path::new("/tmp/oifoif/omar"), Path::new("/tmp/efnnu/omar"),)
.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());
assert!(ConfigClient::new(cfg_path.as_path(), Path::new("/tmp/efnnu/omar")).is_err());
}
#[test]
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 mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path())
.ok()
@ -477,7 +478,7 @@ mod tests {
#[test]
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 mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path())
.ok()
@ -488,7 +489,7 @@ mod tests {
#[test]
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 mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path())
.ok()
@ -502,7 +503,7 @@ mod tests {
#[test]
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 mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path())
.ok()
@ -513,7 +514,7 @@ mod tests {
#[test]
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 mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path())
.ok()
@ -527,7 +528,7 @@ mod tests {
#[test]
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 mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path())
.ok()
@ -540,7 +541,7 @@ mod tests {
#[test]
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 mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path())
.ok()
@ -555,7 +556,7 @@ mod tests {
#[test]
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 mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path())
.ok()
@ -573,7 +574,7 @@ mod tests {
#[test]
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 mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path())
.ok()
@ -637,13 +638,6 @@ mod tests {
(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 {
format!(
"-----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 {
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)]
@ -100,7 +110,7 @@ mod tests {
#[test]
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 mut client: ConfigClient = ConfigClient::new(cfg_path.as_path(), key_path.as_path())
.ok()
@ -128,6 +138,16 @@ mod tests {
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 for configuration and keys directory
@ -138,11 +158,4 @@ mod tests {
c.push("config.toml");
(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]
#[cfg(not(feature = "githubActions"))]
#[cfg(not(feature = "github-actions"))]
fn test_ui_context() {
// Prepare stuff
let mut ctx: Context = Context::new(None, Some(String::from("alles kaput")));

View File

@ -80,7 +80,7 @@ mod tests {
use super::*;
#[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() {
assert!(check_for_updates("100.0.0").ok().unwrap().is_none());
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 random;
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 date_time_str: String = format!("{} {}", tm, this_year);
// Now parse
match NaiveDateTime::parse_from_str(
NaiveDateTime::parse_from_str(
date_time_str.as_ref(),
format!("{} %Y", fmt_hours).as_ref(),
) {
Ok(dt) => dt,
Err(err) => return Err(err),
}
)?
}
};
// 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