When a command was received that only contained newlines it was saved as a valid command.
This pullrequests changes that and strips ending newlines (\n and \r\n) from the command. When that results in an empty string the command will not be saved like any other empty command.
Also added some intigration tests for the client/server.
Fixes #21.
B5GTKRQI4WCU4NUJLJJ22GRPS7DJ72PXE3FRPI3LL7UU2QDAY26QC
EXA2QQEOZ4ZSJNB4X2FYWX25P3ICUSOJJU3Z5CTVH4JTBVZ5QLEQC
F2QYIRKBFYFWSTB7Z5CNGSZVYI6XQO3MHPUHJLBHPGLIOG7UNVNQC
T2HK3ZSDLLLGHROM77MND4UZF663WSHT5J2CT7ZMXDH6MMIZFOAQC
VEN5WJYRT23IT77JAAZ5CJRSW3GUTTNMAECT3WVTHQA34HI4646AC
VCTIMI5CEMFTOMDWYI7S2ZKSFN5XYUXH4GBAIKSEZ2N3SBLSH26QC
FKTISISENWXRFIQHUBCSWQAAN7OWUWIOD6R64YEQOBZ6VENZUWYQC
B2MSSEJB4GIBFA6F2HRMK7FXUHJBZIZFK6NOHY7YZUSHRB53URGQC
PLTSV3RIWYCAUNNYKYL4GHQH5TRPWIDTSDMETCO65ZUQKZQXHLHAC
WSQFCJ4QMS5G3TQO76Z2YHZJTIBIC6GYLNJKUABM2QRNNRHLYIWQC
5WTKSBFRO522ILOHTH5OII4EES3DMLWMTA47PXCVWCIGMXIS77KAC
7V5FJVDUULMBA6XTRS2C625QWWSP3L27QPIINWQHAH2SEFWTTWHQC
IA2CJ4HDSST6MPDLX2FVE2ZZR7EP5O6AIAUFGZIMWJ6NODVMKOKAC
K7M77GF5ILC4KKKYPTLZRZ2OND7DOQDQNRYKM3N6XV2DMJURYA3QC
XJJ6HCY4AYXPWPAGUQEKY2XNPEDV36H6OJKYRPKXC2BIASCLCSMAC
AIF5IVL7B5S2X3N4RLS6GNKUCASQZPOH6NCVGECUFHQ5ZUJVDU6QC
[[package]]
name = "ppv-lite86"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
[[package]]
name = "pretty_assertions"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0cfe1b2403f172ba0f234e500906ee0a3e493fb81092dac23ebefe129301cc"
dependencies = [
"ansi_term 0.12.1",
"ctor",
"diff",
"output_vt100",
]
name = "rand"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
"rand_hc",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
dependencies = [
"getrandom",
]
[[package]]
name = "rand_hc"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
dependencies = [
"rand_core",
]
[[package]]
pub mod client;
pub mod config;
pub mod entry;
pub mod message;
pub mod opt;
pub mod run;
pub mod server;
pub mod store;
use pretty_assertions::assert_eq;
use std::{
path::PathBuf,
sync::{
Arc,
Barrier,
},
thread,
};
use chrono::Utc;
use hstdb::{
client::{
self,
Client,
},
entry::Entry,
message::{
CommandFinished,
CommandStart,
Message,
},
server,
store::{
self,
Filter,
},
};
use uuid::Uuid;
struct TestClient {
client: Client,
barrier_stop: Arc<Barrier>,
cache_dir: PathBuf,
data_dir: PathBuf,
keep_datadir: bool,
}
impl Drop for TestClient {
fn drop(&mut self) {
self.barrier_stop.wait();
std::fs::remove_dir_all(&self.cache_dir).unwrap();
if !self.keep_datadir {
std::fs::remove_dir_all(&self.data_dir).unwrap();
}
}
}
fn create_client_and_server(keep_datadir: bool) -> TestClient {
let cache_dir = tempfile::tempdir().unwrap().into_path();
let data_dir = tempfile::tempdir().unwrap().into_path();
let socket = tempfile::NamedTempFile::new()
.unwrap()
.into_temp_path()
.to_path_buf();
let barrier_start = Arc::new(Barrier::new(2));
let barrier_stop = Arc::new(Barrier::new(2));
{
let barrier_start = Arc::clone(&barrier_start);
let barrier_stop = Arc::clone(&barrier_stop);
let cache_dir = cache_dir.clone();
let data_dir = data_dir.clone();
let socket = socket.clone();
let server = server::builder(cache_dir, data_dir, socket, false)
.build()
.unwrap();
thread::spawn(move || {
barrier_start.wait();
server.run().unwrap();
barrier_stop.wait();
});
}
barrier_start.wait();
let client = client::new(socket);
TestClient {
client,
barrier_stop,
cache_dir,
data_dir,
keep_datadir,
}
}
#[test]
fn stop_server() {
let client = create_client_and_server(false);
client.client.send(&Message::Stop).unwrap();
}
#[test]
fn write_entry() {
let client = create_client_and_server(true);
let session_id = Uuid::new_v4();
let start_data = CommandStart {
command: "Test".to_string(),
pwd: PathBuf::from("/tmp"),
session_id: session_id.clone(),
time_stamp: Utc::now(),
user: "testuser".to_string(),
hostname: "testhostname".to_string(),
};
let finish_data = CommandFinished {
session_id,
time_stamp: Utc::now(),
result: 0,
};
client
.client
.send(&Message::CommandStart(start_data.clone()))
.unwrap();
client
.client
.send(&Message::CommandFinished(finish_data.clone()))
.unwrap();
client.client.send(&Message::Stop).unwrap();
let data_dir = client.data_dir.clone();
drop(client);
let mut entries = store::new(data_dir.clone())
.get_entries(&Filter::default())
.unwrap();
std::fs::remove_dir_all(data_dir).unwrap();
dbg!(&entries);
assert_eq!(entries.len(), 1);
let got = entries.remove(0);
let expected = Entry {
time_finished: finish_data.time_stamp,
time_start: start_data.time_stamp,
hostname: start_data.hostname,
command: start_data.command,
pwd: start_data.pwd,
result: finish_data.result,
session_id: start_data.session_id,
user: start_data.user,
};
assert_eq!(expected, got);
}
#[test]
fn write_entry_whitespace() {
let client = create_client_and_server(true);
let session_id = Uuid::new_v4();
let start_data = CommandStart {
command: r#"Test\nTest\nTest "#.to_string(),
pwd: PathBuf::from("/tmp"),
session_id: session_id.clone(),
time_stamp: Utc::now(),
user: "testuser".to_string(),
hostname: "testhostname".to_string(),
};
let finish_data = CommandFinished {
session_id,
time_stamp: Utc::now(),
result: 0,
};
client
.client
.send(&Message::CommandStart(start_data.clone()))
.unwrap();
client
.client
.send(&Message::CommandFinished(finish_data.clone()))
.unwrap();
client.client.send(&Message::Stop).unwrap();
let data_dir = client.data_dir.clone();
drop(client);
let mut entries = store::new(data_dir.clone())
.get_entries(&Filter::default())
.unwrap();
std::fs::remove_dir_all(data_dir).unwrap();
dbg!(&entries);
assert_eq!(entries.len(), 1);
let got = entries.remove(0);
let expected = Entry {
time_finished: finish_data.time_stamp,
time_start: start_data.time_stamp,
hostname: start_data.hostname,
command: r#"Test\nTest\nTest"#.to_string(),
pwd: start_data.pwd,
result: finish_data.result,
session_id: start_data.session_id,
user: start_data.user,
};
assert_eq!(expected, got);
}
// TODO: Make a test for this probably needs a restructuring of how we
// detect leading spaces in commands
//#[test]
// fn write_command_starting_spaces() {
// let client = create_client_and_server(true);
//
// let session_id = Uuid::new_v4();
//
// let start_data = CommandStart {
// command: " Test".to_string(),
// pwd: PathBuf::from("/tmp"),
// session_id: session_id.clone(),
// time_stamp: Utc::now(),
// user: "testuser".to_string(),
// hostname: "testhostname".to_string(),
// };
//
// let finish_data = CommandFinished {
// session_id,
// time_stamp: Utc::now(),
// result: 0,
// };
//
// client
// .client
// .send(&Message::CommandStart(start_data.clone()))
// .unwrap();
//
// client
// .client
// .send(&Message::CommandFinished(finish_data.clone()))
// .unwrap();
//
// client.client.send(&Message::Stop).unwrap();
//
// let data_dir = client.data_dir.clone();
// drop(client);
//
// let entries = store::new(data_dir.clone())
// .get_entries(&Filter::default())
// .unwrap();
//
// std::fs::remove_dir_all(data_dir).unwrap();
//
// dbg!(&entries);
//
// assert_eq!(entries.len(), 0);
//}
#[test]
fn write_empty_command() {
let client = create_client_and_server(true);
let session_id = Uuid::new_v4();
let start_data = CommandStart {
command: "".to_string(),
pwd: PathBuf::from("/tmp"),
session_id: session_id.clone(),
time_stamp: Utc::now(),
user: "testuser".to_string(),
hostname: "testhostname".to_string(),
};
let finish_data = CommandFinished {
session_id,
time_stamp: Utc::now(),
result: 0,
};
client
.client
.send(&Message::CommandStart(start_data.clone()))
.unwrap();
client
.client
.send(&Message::CommandFinished(finish_data.clone()))
.unwrap();
client.client.send(&Message::Stop).unwrap();
let data_dir = client.data_dir.clone();
drop(client);
let entries = store::new(data_dir.clone())
.get_entries(&Filter::default())
.unwrap();
std::fs::remove_dir_all(data_dir).unwrap();
dbg!(&entries);
assert_eq!(entries.len(), 0);
}
#[test]
fn write_newline_command() {
let client = create_client_and_server(true);
let session_id = Uuid::new_v4();
let commands = vec![
"\n".to_string(),
"\r\n".to_string(),
"\n\n".to_string(),
"\n\n\n".to_string(),
r#"\n"#.to_string(),
'\n'.to_string(),
'\r'.to_string(),
];
for command in commands {
let start_data = CommandStart {
command,
pwd: PathBuf::from("/tmp"),
session_id: session_id.clone(),
time_stamp: Utc::now(),
user: "testuser".to_string(),
hostname: "testhostname".to_string(),
};
let finish_data = CommandFinished {
session_id,
time_stamp: Utc::now(),
result: 0,
};
client
.client
.send(&Message::CommandStart(start_data.clone()))
.unwrap();
client
.client
.send(&Message::CommandFinished(finish_data.clone()))
.unwrap();
}
client.client.send(&Message::Stop).unwrap();
let data_dir = client.data_dir.clone();
drop(client);
let entries = store::new(data_dir.clone())
.get_entries(&Filter::default())
.unwrap();
std::fs::remove_dir_all(data_dir).unwrap();
dbg!(&entries);
assert_eq!(entries.len(), 0);
}