From 047067fa4fa18ca7aa35f6b51f1cf9ab7e28d50f Mon Sep 17 00:00:00 2001 From: Kurisu Date: Thu, 29 Aug 2024 17:29:00 +0800 Subject: [PATCH 1/4] test: add ssh integration tests and . --- Cargo.lock | 54 ++++++++++----------- Cargo.toml | 7 +++ Makefile | 24 +++++++++ src/lib.rs | 10 ++++ tests/key_session_integration_tests.rs | 28 +++++++++++ tests/password_session_integration_tests.rs | 26 ++++++++++ 6 files changed, 122 insertions(+), 27 deletions(-) create mode 100644 Makefile create mode 100644 src/lib.rs create mode 100644 tests/key_session_integration_tests.rs create mode 100644 tests/password_session_integration_tests.rs diff --git a/Cargo.lock b/Cargo.lock index 31486ff..4658371 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1204,7 +1204,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -2171,7 +2171,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -2191,18 +2191,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -2213,9 +2213,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -2225,9 +2225,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -2237,15 +2237,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -2255,9 +2255,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -2267,9 +2267,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -2279,9 +2279,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -2291,9 +2291,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" diff --git a/Cargo.toml b/Cargo.toml index 432579b..5e91c63 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,13 @@ name = "ssh-utils" version = "0.1.0" edition = "2021" +[lib] +name = "ssh_utils_lib" +path = "src/lib.rs" + +[features] +integration_tests = [] + [dependencies] anyhow = "1.0.86" backtrace = "0.3.73" diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d8bd8b1 --- /dev/null +++ b/Makefile @@ -0,0 +1,24 @@ +.PHONY: test integration_tests + +test: + @echo "Running regular tests..." + cargo test + +integration_test: + @echo "Please enter the following information for integration tests:" + @read -p "SSH username: " ssh_user && \ + read -p "SSH key path: " ssh_key_path && \ + read -p "SSH server address: " ssh_addr && \ + stty -echo && \ + read -p "SSH password: " ssh_password && \ + stty echo && \ + echo && \ + export SSH_TEST_USER="$$ssh_user" && \ + export SSH_TEST_KEY_PATH="$$ssh_key_path" && \ + export SSH_TEST_ADDR="$$ssh_addr" && \ + export SSH_TEST_PASSWORD="$$ssh_password" && \ + echo "Running integration tests..." && \ + cargo test --features integration_tests -- --nocapture + +build: + cargo build --release \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..4d752de --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,10 @@ +pub mod app; +pub mod config; +pub mod helper; +pub mod macros; +pub mod ssh; +pub mod widgets; + +// 导出需要测试的模块和函数 +pub use ssh::key_session::KeySession; +pub use ssh::ssh_session::{AuthMethod, SshSession}; \ No newline at end of file diff --git a/tests/key_session_integration_tests.rs b/tests/key_session_integration_tests.rs new file mode 100644 index 0000000..8dea3b1 --- /dev/null +++ b/tests/key_session_integration_tests.rs @@ -0,0 +1,28 @@ +#[cfg(feature = "integration_tests")] +mod tests { + use russh_keys::load_secret_key; +use ssh_utils_lib::{ + ssh::key_session::KeySession, + ssh::ssh_session::{AuthMethod, SshSession}, +}; +use std::env; + +#[tokio::test] +async fn test_key_session_integration() { + let user = env::var("SSH_TEST_USER").expect("SSH_TEST_USER not set"); + let key_path = env::var("SSH_TEST_KEY_PATH").expect("SSH_TEST_KEY_PATH not set"); + let addr = env::var("SSH_TEST_ADDR").expect("SSH_TEST_ADDR not set"); + + let key = load_secret_key(key_path.clone(), None).expect("Failed to load secret key"); + let auth = AuthMethod::Key(key); + + let mut session = KeySession::connect(user, auth, addr).await.expect("Failed to connect"); + + // 测试执行命令 + let exit_code = session.call("echo 'Hello, World!'").await.expect("Failed to execute command"); + assert_eq!(exit_code, 0); + + // 关闭会话 + session.close().await.expect("Failed to close session"); +} +} \ No newline at end of file diff --git a/tests/password_session_integration_tests.rs b/tests/password_session_integration_tests.rs new file mode 100644 index 0000000..cefd309 --- /dev/null +++ b/tests/password_session_integration_tests.rs @@ -0,0 +1,26 @@ +#[cfg(feature = "integration_tests")] +mod tests { + use ssh_utils_lib::{ + ssh::password_session::PasswordSession, + ssh::ssh_session::{AuthMethod, SshSession}, + }; + use std::env; + + #[tokio::test] + async fn test_password_session_integration() { + let user = env::var("SSH_TEST_USER").expect("SSH_TEST_USER not set"); + let password = env::var("SSH_TEST_PASSWORD").expect("SSH_TEST_PASSWORD not set"); + let addr = env::var("SSH_TEST_ADDR").expect("SSH_TEST_ADDR not set"); + + let auth = AuthMethod::Password(password); + + let mut session = PasswordSession::connect(user, auth, addr).await.expect("Failed to connect"); + + // 测试执行命令 + let exit_code = session.call("echo 'Hello, World!'").await.expect("Failed to execute command"); + assert_eq!(exit_code, 0); + + // 关闭会话 + session.close().await.expect("Failed to close session"); + } +} \ No newline at end of file -- Gitee From 2327951bf69445a0b4e3ec167533e94d0f58e9cb Mon Sep 17 00:00:00 2001 From: Kurisu Date: Thu, 29 Aug 2024 17:53:11 +0800 Subject: [PATCH 2/4] test: add test_find_best_key(). --- Cargo.lock | 29 +++++++++++++++++++++++++++++ Cargo.toml | 3 ++- src/app.rs | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 4658371..5020f20 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -627,6 +627,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "fastrand" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + [[package]] name = "ff" version = "0.13.0" @@ -1895,6 +1901,7 @@ dependencies = [ "rust-argon2", "serde", "sha2", + "tempfile", "tokio", "tokio-fd", "toml", @@ -1953,6 +1960,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + [[package]] name = "thiserror" version = "1.0.63" @@ -2174,6 +2194,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-targets" version = "0.48.5" diff --git a/Cargo.toml b/Cargo.toml index 5e91c63..31460c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,4 +32,5 @@ russh = "0.45.0" russh-keys = "0.45.0" tokio = { version = "1.36.0", features = ["full"] } async-trait = "0.1.81" -tokio-fd = "0.3.0" \ No newline at end of file +tokio-fd = "0.3.0" +tempfile = "3.12.0" \ No newline at end of file diff --git a/src/app.rs b/src/app.rs index 2f0fde7..aec8c3c 100644 --- a/src/app.rs +++ b/src/app.rs @@ -552,3 +552,40 @@ fn load_key_with_passphrase( } }) } + +#[cfg(test)] +mod tests { + use super::*; + use std::fs::File; + use tempfile::TempDir; + + #[test] + fn test_find_best_key() { + // Create a temporary directory to simulate the home directory + let temp_dir = TempDir::new().unwrap(); + let home_dir = temp_dir.path(); + let ssh_dir = home_dir.join(".ssh"); + std::fs::create_dir(&ssh_dir).unwrap(); + + // Simulate environment variable + std::env::set_var("HOME", home_dir.to_str().unwrap()); + + // Test scenario 1: No key files present + assert_eq!(find_best_key(), None); + + // Test scenario 2: Only id_rsa present + File::create(ssh_dir.join("id_rsa")).unwrap(); + assert_eq!(find_best_key(), Some(ssh_dir.join("id_rsa"))); + + // Test scenario 3: Both id_rsa and id_ed25519 present + File::create(ssh_dir.join("id_ed25519")).unwrap(); + assert_eq!(find_best_key(), Some(ssh_dir.join("id_ed25519"))); + + // Test scenario 4: Multiple keys present, should select the highest priority one + File::create(ssh_dir.join("id_ecdsa")).unwrap(); + assert_eq!(find_best_key(), Some(ssh_dir.join("id_ecdsa"))); + + // Cleanup + temp_dir.close().unwrap(); + } +} -- Gitee From 3b9d14811a24deec10e577e2c508e1f672f5c77b Mon Sep 17 00:00:00 2001 From: Kurisu Date: Thu, 29 Aug 2024 17:53:21 +0800 Subject: [PATCH 3/4] test: add config file tests. --- src/config/app_config.rs | 65 +++++++++++++++++++++++++++++++++++----- src/config/crypto.rs | 20 ++++++------- src/macros.rs | 6 ++-- 3 files changed, 70 insertions(+), 21 deletions(-) diff --git a/src/config/app_config.rs b/src/config/app_config.rs index 50977e7..d387d16 100644 --- a/src/config/app_config.rs +++ b/src/config/app_config.rs @@ -1,7 +1,7 @@ use anyhow::{Context, Result}; use serde::{Deserialize, Serialize}; use uuid::Uuid; -use std::{fs, path::PathBuf}; +use std::{fs, path::{Path, PathBuf}}; use crate::helper::{get_file_path, CONFIG_FILE}; @@ -136,18 +136,24 @@ pub fn ensure_config_exists() -> Result<()> { from "~/.config/ssh-utils/config.toml" */ pub fn read_config() -> Result { - let config_path = if cfg!(debug_assertions) { - ".config/ssh-utils/config.toml".into() + let config_path = get_config_path()?; + read_config_from_path(&config_path) +} + +fn get_config_path() -> Result { + if cfg!(debug_assertions) { + Ok(".config/ssh-utils/config.toml".into()) } else { let mut path = dirs::home_dir().context("Unable to reach user's home directory.")?; path.push(".config/ssh-utils/config.toml"); - path - }; + Ok(path) + } +} +fn read_config_from_path>(config_path: P) -> Result { let config_str = fs::read_to_string(&config_path) - .with_context(|| format!("Unable to read ssh-utils' config file at {:?}", config_path))?; + .with_context(|| format!("Unable to read ssh-utils' config file at {:?}", config_path.as_ref()))?; - // Check if the config file content is empty if config_str.trim().is_empty() { return Ok(Config::default()); } @@ -157,3 +163,48 @@ pub fn read_config() -> Result { Ok(config) } + +#[cfg(test)] +mod tests { + use super::*; + use tempfile::TempDir; + use std::fs; + + #[test] + fn test_read_config_empty_file() { + let temp_dir = TempDir::new().unwrap(); + let config_path = temp_dir.path().join("config.toml"); + fs::write(&config_path, "").unwrap(); + let config = read_config_from_path(&config_path).unwrap(); + assert!(config.servers.is_empty()); + } + + #[test] + fn test_read_config_with_servers() { + let temp_dir = TempDir::new().unwrap(); + let config_path = temp_dir.path().join("config.toml"); + let config_content = r#" + [[servers]] + id = "1" + name = "Server1" + ip = "192.168.1.1" + user = "user1" + shell = "/bin/bash" + port = 22 + + [[servers]] + id = "2" + name = "Server2" + ip = "192.168.1.2" + user = "user2" + shell = "/bin/zsh" + port = 2222 + "#; + fs::write(&config_path, config_content).unwrap(); + + let config = read_config_from_path(&config_path).unwrap(); + assert_eq!(config.servers.len(), 2); + assert_eq!(config.servers[0].name, "Server1"); + assert_eq!(config.servers[1].port, 2222); + } +} \ No newline at end of file diff --git a/src/config/crypto.rs b/src/config/crypto.rs index a37b891..47258b3 100644 --- a/src/config/crypto.rs +++ b/src/config/crypto.rs @@ -74,7 +74,9 @@ pub fn aes_decrypt(key: &[u8], iv: &[u8], data: &[u8]) -> Result> { #[cfg(test)] mod tests { - use std::{fs, process::Command}; + use std::{io::Write, process::Command}; + + use tempfile::NamedTempFile; use super::*; @@ -146,9 +148,9 @@ mod tests { let data = b"Hello, AES encryption!"; // Write data to a temporary file - let data_file_path = "data.txt"; - let mut file = fs::File::create(data_file_path).expect("Failed to create file"); - std::io::Write::write_all(&mut file, data).expect("Failed to write data to file"); + let mut data_file = NamedTempFile::new().expect("Failed to create temporary file"); + data_file.write_all(data).expect("Failed to write data to file"); + let data_file_path = data_file.path().to_str().unwrap(); // Use OpenSSL to generate the expected encrypted value let expected_encrypted_output = Command::new("openssl") @@ -170,9 +172,9 @@ mod tests { assert_eq!(encrypted_data, expected_encrypted_data, "Encrypted data should match the expected value"); // Write encrypted_data to a temporary file - let encrypted_file_path = "encrypted_data.bin"; - let mut encrypted_file = fs::File::create(encrypted_file_path).expect("Failed to create file"); - std::io::Write::write_all(&mut encrypted_file, &encrypted_data).expect("Failed to write encrypted data to file"); + let mut encrypted_file = NamedTempFile::new().expect("Failed to create temporary file"); + encrypted_file.write_all(&encrypted_data).expect("Failed to write encrypted data to file"); + let encrypted_file_path = encrypted_file.path().to_str().unwrap(); // Use OpenSSL to generate the expected decrypted value let expected_decrypted_output = Command::new("openssl") @@ -193,9 +195,5 @@ mod tests { let decrypted_data = aes_decrypt(key, iv, &encrypted_data).expect("Failed to decrypt data"); assert_eq!(decrypted_data, data, "Decrypted data should match original data"); assert_eq!(decrypted_data, expected_decrypted_data, "Decrypted data should match the expected value"); - - // Delete temporary files - fs::remove_file(data_file_path).expect("Failed to delete data file"); - fs::remove_file(encrypted_file_path).expect("Failed to delete encrypted file"); } } \ No newline at end of file diff --git a/src/macros.rs b/src/macros.rs index eb83ba5..9866caf 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -8,9 +8,9 @@ macro_rules! debug_log { .append(true) .open($file) .await - .expect("无法打开日志文件"); - debug_file.write_all(format!($($arg)*).as_bytes()).await.expect("写入失败"); - debug_file.write_all(b"\n").await.expect("写入失败"); + .expect("can't open log file"); + debug_file.write_all(format!($($arg)*).as_bytes()).await.expect("failed to write log"); + debug_file.write_all(b"\n").await.expect("failed to write new line"); } }; } \ No newline at end of file -- Gitee From 8dcb88cd3414486754701caca886a7a22b1ee24c Mon Sep 17 00:00:00 2001 From: Kurisu Date: Thu, 29 Aug 2024 18:08:01 +0800 Subject: [PATCH 4/4] chore: wrong make name in Makefile. --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index d8bd8b1..bdb44dd 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,10 @@ -.PHONY: test integration_tests +.PHONY: test ssh_test test: @echo "Running regular tests..." cargo test -integration_test: +ssh_test: @echo "Please enter the following information for integration tests:" @read -p "SSH username: " ssh_user && \ read -p "SSH key path: " ssh_key_path && \ -- Gitee