Fix file-write and file-append not parsing input correctly

pull/38/head
Chip Senkbeil 3 years ago
parent da08d2db4f
commit 32150f0956
No known key found for this signature in database
GPG Key ID: 35EF1F8EC72A4131

@ -11,7 +11,7 @@ readme = "README.md"
license = "MIT OR Apache-2.0"
[dependencies]
bytes = "1.0.1"
bytes = "1.1.0"
derive_more = { version = "0.99.16", default-features = false, features = ["display", "from", "error", "is_variant"] }
futures = "0.3.16"
hex = "0.4.3"

@ -3,6 +3,16 @@ use serde::{Deserialize, Serialize};
use std::{io, path::PathBuf};
use strum::AsRefStr;
/// Type alias for a vec of bytes
///
/// NOTE: This only exists to support properly parsing a Vec<u8> from an entire string
/// with structopt rather than trying to parse a string as a singular u8
pub type ByteVec = Vec<u8>;
fn parse_byte_vec(src: &str) -> ByteVec {
src.as_bytes().to_vec()
}
/// Represents the request to be performed on the remote machine
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case", deny_unknown_fields)]
@ -70,7 +80,8 @@ pub enum RequestData {
path: PathBuf,
/// Data for server-side writing of content
data: Vec<u8>,
#[cfg_attr(feature = "structopt", structopt(parse(from_str = parse_byte_vec)))]
data: ByteVec,
},
/// Writes a file using text instead of bytes, creating it if it does not exist,
@ -89,7 +100,8 @@ pub enum RequestData {
path: PathBuf,
/// Data for server-side writing of content
data: Vec<u8>,
#[cfg_attr(feature = "structopt", structopt(parse(from_str = parse_byte_vec)))]
data: ByteVec,
},
/// Appends text to a file, creating it if it does not exist, on the remote machine

@ -0,0 +1,143 @@
use crate::cli::{
fixtures::*,
utils::{random_tenant, FAILURE_LINE},
};
use assert_cmd::Command;
use assert_fs::prelude::*;
use distant::ExitCode;
use distant_core::{
data::{Error, ErrorKind},
Request, RequestData, Response, ResponseData,
};
use rstest::*;
const FILE_CONTENTS: &str = r#"
some text
on multiple lines
that is a file's contents
"#;
const APPENDED_FILE_CONTENTS: &str = r#"
even more
file contents
"#;
#[rstest]
fn should_report_ok_when_done(mut action_cmd: Command) {
let temp = assert_fs::TempDir::new().unwrap();
let file = temp.child("test-file");
file.write_str(FILE_CONTENTS).unwrap();
// distant action file-append {path} -- {contents}
action_cmd
.args(&[
"file-append",
file.to_str().unwrap(),
"--",
APPENDED_FILE_CONTENTS,
])
.assert()
.success()
.stdout("")
.stderr("");
// Because we're talking to a local server, we can verify locally
file.assert(format!("{}{}", FILE_CONTENTS, APPENDED_FILE_CONTENTS));
}
#[rstest]
fn should_support_json_output(mut action_cmd: Command) {
let temp = assert_fs::TempDir::new().unwrap();
let file = temp.child("test-file");
file.write_str(FILE_CONTENTS).unwrap();
let req = Request {
id: rand::random(),
tenant: random_tenant(),
payload: vec![RequestData::FileAppend {
path: file.to_path_buf(),
data: APPENDED_FILE_CONTENTS.as_bytes().to_vec(),
}],
};
// distant action --format json --interactive
let cmd = action_cmd
.args(&["--format", "json"])
.arg("--interactive")
.write_stdin(format!("{}\n", serde_json::to_string(&req).unwrap()))
.assert()
.success()
.stderr("");
let res: Response = serde_json::from_slice(&cmd.get_output().stdout).unwrap();
assert!(
matches!(res.payload[0], ResponseData::Ok),
"Unexpected response: {:?}",
res.payload[0]
);
// Because we're talking to a local server, we can verify locally
file.assert(format!("{}{}", FILE_CONTENTS, APPENDED_FILE_CONTENTS));
}
#[rstest]
fn yield_an_error_when_fails(mut action_cmd: Command) {
let temp = assert_fs::TempDir::new().unwrap();
let file = temp.child("missing-dir").child("missing-file");
// distant action file-append {path} -- {contents}
action_cmd
.args(&[
"file-append",
file.to_str().unwrap(),
"--",
APPENDED_FILE_CONTENTS,
])
.assert()
.code(ExitCode::Software.to_i32())
.stdout("")
.stderr(FAILURE_LINE.clone());
// Because we're talking to a local server, we can verify locally
file.assert(predicates::path::missing());
}
#[rstest]
fn should_support_json_output_for_error(mut action_cmd: Command) {
let temp = assert_fs::TempDir::new().unwrap();
let file = temp.child("missing-dir").child("missing-file");
let req = Request {
id: rand::random(),
tenant: random_tenant(),
payload: vec![RequestData::FileAppend {
path: file.to_path_buf(),
data: APPENDED_FILE_CONTENTS.as_bytes().to_vec(),
}],
};
// distant action --format json --interactive
let cmd = action_cmd
.args(&["--format", "json"])
.arg("--interactive")
.write_stdin(format!("{}\n", serde_json::to_string(&req).unwrap()))
.assert()
.success()
.stderr("");
let res: Response = serde_json::from_slice(&cmd.get_output().stdout).unwrap();
assert!(
matches!(
res.payload[0],
ResponseData::Error(Error {
kind: ErrorKind::NotFound,
..
})
),
"Unexpected response: {:?}",
res.payload[0]
);
// Because we're talking to a local server, we can verify locally
file.assert(predicates::path::missing());
}

@ -0,0 +1,143 @@
use crate::cli::{
fixtures::*,
utils::{random_tenant, FAILURE_LINE},
};
use assert_cmd::Command;
use assert_fs::prelude::*;
use distant::ExitCode;
use distant_core::{
data::{Error, ErrorKind},
Request, RequestData, Response, ResponseData,
};
use rstest::*;
const FILE_CONTENTS: &str = r#"
some text
on multiple lines
that is a file's contents
"#;
const APPENDED_FILE_CONTENTS: &str = r#"
even more
file contents
"#;
#[rstest]
fn should_report_ok_when_done(mut action_cmd: Command) {
let temp = assert_fs::TempDir::new().unwrap();
let file = temp.child("test-file");
file.write_str(FILE_CONTENTS).unwrap();
// distant action file-append-text {path} -- {contents}
action_cmd
.args(&[
"file-append-text",
file.to_str().unwrap(),
"--",
APPENDED_FILE_CONTENTS,
])
.assert()
.success()
.stdout("")
.stderr("");
// Because we're talking to a local server, we can verify locally
file.assert(format!("{}{}", FILE_CONTENTS, APPENDED_FILE_CONTENTS));
}
#[rstest]
fn should_support_json_output(mut action_cmd: Command) {
let temp = assert_fs::TempDir::new().unwrap();
let file = temp.child("test-file");
file.write_str(FILE_CONTENTS).unwrap();
let req = Request {
id: rand::random(),
tenant: random_tenant(),
payload: vec![RequestData::FileAppendText {
path: file.to_path_buf(),
text: APPENDED_FILE_CONTENTS.to_string(),
}],
};
// distant action --format json --interactive
let cmd = action_cmd
.args(&["--format", "json"])
.arg("--interactive")
.write_stdin(format!("{}\n", serde_json::to_string(&req).unwrap()))
.assert()
.success()
.stderr("");
let res: Response = serde_json::from_slice(&cmd.get_output().stdout).unwrap();
assert!(
matches!(res.payload[0], ResponseData::Ok),
"Unexpected response: {:?}",
res.payload[0]
);
// Because we're talking to a local server, we can verify locally
file.assert(format!("{}{}", FILE_CONTENTS, APPENDED_FILE_CONTENTS));
}
#[rstest]
fn yield_an_error_when_fails(mut action_cmd: Command) {
let temp = assert_fs::TempDir::new().unwrap();
let file = temp.child("missing-dir").child("missing-file");
// distant action file-append-text {path} -- {contents}
action_cmd
.args(&[
"file-append-text",
file.to_str().unwrap(),
"--",
APPENDED_FILE_CONTENTS,
])
.assert()
.code(ExitCode::Software.to_i32())
.stdout("")
.stderr(FAILURE_LINE.clone());
// Because we're talking to a local server, we can verify locally
file.assert(predicates::path::missing());
}
#[rstest]
fn should_support_json_output_for_error(mut action_cmd: Command) {
let temp = assert_fs::TempDir::new().unwrap();
let file = temp.child("missing-dir").child("missing-file");
let req = Request {
id: rand::random(),
tenant: random_tenant(),
payload: vec![RequestData::FileAppendText {
path: file.to_path_buf(),
text: APPENDED_FILE_CONTENTS.to_string(),
}],
};
// distant action --format json --interactive
let cmd = action_cmd
.args(&["--format", "json"])
.arg("--interactive")
.write_stdin(format!("{}\n", serde_json::to_string(&req).unwrap()))
.assert()
.success()
.stderr("");
let res: Response = serde_json::from_slice(&cmd.get_output().stdout).unwrap();
assert!(
matches!(
res.payload[0],
ResponseData::Error(Error {
kind: ErrorKind::NotFound,
..
})
),
"Unexpected response: {:?}",
res.payload[0]
);
// Because we're talking to a local server, we can verify locally
file.assert(predicates::path::missing());
}

@ -1,10 +1,13 @@
use crate::cli::{fixtures::*, utils::FAILURE_LINE};
use crate::cli::{
fixtures::*,
utils::{random_tenant, FAILURE_LINE},
};
use assert_cmd::Command;
use assert_fs::prelude::*;
use distant::ExitCode;
use distant_core::{
data::{Error, ErrorKind},
Response, ResponseData,
Request, RequestData, Response, ResponseData,
};
use rstest::*;
@ -35,10 +38,19 @@ fn should_support_json_output(mut action_cmd: Command) {
let file = temp.child("test-file");
file.write_str(FILE_CONTENTS).unwrap();
// distant action --format json file-read {path}
let req = Request {
id: rand::random(),
tenant: random_tenant(),
payload: vec![RequestData::FileRead {
path: file.to_path_buf(),
}],
};
// distant action --format json --interactive
let cmd = action_cmd
.args(&["--format", "json"])
.args(&["file-read", file.to_str().unwrap()])
.arg("--interactive")
.write_stdin(format!("{}\n", serde_json::to_string(&req).unwrap()))
.assert()
.success()
.stderr("");
@ -71,12 +83,21 @@ fn should_support_json_output_for_error(mut action_cmd: Command) {
let temp = assert_fs::TempDir::new().unwrap();
let file = temp.child("missing-file");
// distant action --format json file-read {path}
let req = Request {
id: rand::random(),
tenant: random_tenant(),
payload: vec![RequestData::FileRead {
path: file.to_path_buf(),
}],
};
// distant action --format json --interactive
let cmd = action_cmd
.args(&["--format", "json"])
.args(&["file-read", file.to_str().unwrap()])
.arg("--interactive")
.write_stdin(format!("{}\n", serde_json::to_string(&req).unwrap()))
.assert()
.code(ExitCode::Software.to_i32())
.success()
.stderr("");
let res: Response = serde_json::from_slice(&cmd.get_output().stdout).unwrap();

@ -1,10 +1,13 @@
use crate::cli::{fixtures::*, utils::FAILURE_LINE};
use crate::cli::{
fixtures::*,
utils::{random_tenant, FAILURE_LINE},
};
use assert_cmd::Command;
use assert_fs::prelude::*;
use distant::ExitCode;
use distant_core::{
data::{Error, ErrorKind},
Response, ResponseData,
Request, RequestData, Response, ResponseData,
};
use rstest::*;
@ -35,10 +38,19 @@ fn should_support_json_output(mut action_cmd: Command) {
let file = temp.child("test-file");
file.write_str(FILE_CONTENTS).unwrap();
// distant action --format json file-read-text {path}
let req = Request {
id: rand::random(),
tenant: random_tenant(),
payload: vec![RequestData::FileReadText {
path: file.to_path_buf(),
}],
};
// distant action --format json --interactive
let cmd = action_cmd
.args(&["--format", "json"])
.args(&["file-read-text", file.to_str().unwrap()])
.arg("--interactive")
.write_stdin(format!("{}\n", serde_json::to_string(&req).unwrap()))
.assert()
.success()
.stderr("");
@ -71,12 +83,21 @@ fn should_support_json_output_for_error(mut action_cmd: Command) {
let temp = assert_fs::TempDir::new().unwrap();
let file = temp.child("missing-file");
// distant action --format json file-read-text {path}
let req = Request {
id: rand::random(),
tenant: random_tenant(),
payload: vec![RequestData::FileReadText {
path: file.to_path_buf(),
}],
};
// distant action --format json --interactive
let cmd = action_cmd
.args(&["--format", "json"])
.args(&["file-read-text", file.to_str().unwrap()])
.arg("--interactive")
.write_stdin(format!("{}\n", serde_json::to_string(&req).unwrap()))
.assert()
.code(ExitCode::Software.to_i32())
.success()
.stderr("");
let res: Response = serde_json::from_slice(&cmd.get_output().stdout).unwrap();

@ -0,0 +1,126 @@
use crate::cli::{
fixtures::*,
utils::{random_tenant, FAILURE_LINE},
};
use assert_cmd::Command;
use assert_fs::prelude::*;
use distant::ExitCode;
use distant_core::{
data::{Error, ErrorKind},
Request, RequestData, Response, ResponseData,
};
use rstest::*;
const FILE_CONTENTS: &str = r#"
some text
on multiple lines
that is a file's contents
"#;
#[rstest]
fn should_report_ok_when_done(mut action_cmd: Command) {
let temp = assert_fs::TempDir::new().unwrap();
let file = temp.child("test-file");
// distant action file-write {path} -- {contents}
action_cmd
.args(&["file-write", file.to_str().unwrap(), "--", FILE_CONTENTS])
.assert()
.success()
.stdout("")
.stderr("");
// Because we're talking to a local server, we can verify locally
file.assert(FILE_CONTENTS);
}
#[rstest]
fn should_support_json_output(mut action_cmd: Command) {
let temp = assert_fs::TempDir::new().unwrap();
let file = temp.child("test-file");
let req = Request {
id: rand::random(),
tenant: random_tenant(),
payload: vec![RequestData::FileWrite {
path: file.to_path_buf(),
data: FILE_CONTENTS.as_bytes().to_vec(),
}],
};
// distant action --format json --interactive
let cmd = action_cmd
.args(&["--format", "json"])
.arg("--interactive")
.write_stdin(format!("{}\n", serde_json::to_string(&req).unwrap()))
.assert()
.success()
.stderr("");
let res: Response = serde_json::from_slice(&cmd.get_output().stdout).unwrap();
assert!(
matches!(res.payload[0], ResponseData::Ok),
"Unexpected response: {:?}",
res.payload[0]
);
// Because we're talking to a local server, we can verify locally
file.assert(FILE_CONTENTS);
}
#[rstest]
fn yield_an_error_when_fails(mut action_cmd: Command) {
let temp = assert_fs::TempDir::new().unwrap();
let file = temp.child("missing-dir").child("missing-file");
// distant action file-write {path} -- {contents}
action_cmd
.args(&["file-write", file.to_str().unwrap(), "--", FILE_CONTENTS])
.assert()
.code(ExitCode::Software.to_i32())
.stdout("")
.stderr(FAILURE_LINE.clone());
// Because we're talking to a local server, we can verify locally
file.assert(predicates::path::missing());
}
#[rstest]
fn should_support_json_output_for_error(mut action_cmd: Command) {
let temp = assert_fs::TempDir::new().unwrap();
let file = temp.child("missing-dir").child("missing-file");
let req = Request {
id: rand::random(),
tenant: random_tenant(),
payload: vec![RequestData::FileWrite {
path: file.to_path_buf(),
data: FILE_CONTENTS.as_bytes().to_vec(),
}],
};
// distant action --format json --interactive
let cmd = action_cmd
.args(&["--format", "json"])
.arg("--interactive")
.write_stdin(format!("{}\n", serde_json::to_string(&req).unwrap()))
.assert()
.success()
.stderr("");
let res: Response = serde_json::from_slice(&cmd.get_output().stdout).unwrap();
assert!(
matches!(
res.payload[0],
ResponseData::Error(Error {
kind: ErrorKind::NotFound,
..
})
),
"Unexpected response: {:?}",
res.payload[0]
);
// Because we're talking to a local server, we can verify locally
file.assert(predicates::path::missing());
}

@ -0,0 +1,136 @@
use crate::cli::{
fixtures::*,
utils::{random_tenant, FAILURE_LINE},
};
use assert_cmd::Command;
use assert_fs::prelude::*;
use distant::ExitCode;
use distant_core::{
data::{Error, ErrorKind},
Request, RequestData, Response, ResponseData,
};
use rstest::*;
const FILE_CONTENTS: &str = r#"
some text
on multiple lines
that is a file's contents
"#;
#[rstest]
fn should_report_ok_when_done(mut action_cmd: Command) {
let temp = assert_fs::TempDir::new().unwrap();
let file = temp.child("test-file");
// distant action file-write-text {path} -- {contents}
action_cmd
.args(&[
"file-write-text",
file.to_str().unwrap(),
"--",
FILE_CONTENTS,
])
.assert()
.success()
.stdout("")
.stderr("");
// Because we're talking to a local server, we can verify locally
file.assert(FILE_CONTENTS);
}
#[rstest]
fn should_support_json_output(mut action_cmd: Command) {
let temp = assert_fs::TempDir::new().unwrap();
let file = temp.child("test-file");
let req = Request {
id: rand::random(),
tenant: random_tenant(),
payload: vec![RequestData::FileWriteText {
path: file.to_path_buf(),
text: FILE_CONTENTS.to_string(),
}],
};
// distant action --format json --interactive
let cmd = action_cmd
.args(&["--format", "json"])
.arg("--interactive")
.write_stdin(format!("{}\n", serde_json::to_string(&req).unwrap()))
.assert()
.success()
.stderr("");
let res: Response = serde_json::from_slice(&cmd.get_output().stdout).unwrap();
assert!(
matches!(res.payload[0], ResponseData::Ok),
"Unexpected response: {:?}",
res.payload[0]
);
// Because we're talking to a local server, we can verify locally
file.assert(FILE_CONTENTS);
}
#[rstest]
fn yield_an_error_when_fails(mut action_cmd: Command) {
let temp = assert_fs::TempDir::new().unwrap();
let file = temp.child("missing-dir").child("missing-file");
// distant action file-write {path} -- {contents}
action_cmd
.args(&[
"file-write-text",
file.to_str().unwrap(),
"--",
FILE_CONTENTS,
])
.assert()
.code(ExitCode::Software.to_i32())
.stdout("")
.stderr(FAILURE_LINE.clone());
// Because we're talking to a local server, we can verify locally
file.assert(predicates::path::missing());
}
#[rstest]
fn should_support_json_output_for_error(mut action_cmd: Command) {
let temp = assert_fs::TempDir::new().unwrap();
let file = temp.child("missing-dir").child("missing-file");
let req = Request {
id: rand::random(),
tenant: random_tenant(),
payload: vec![RequestData::FileWriteText {
path: file.to_path_buf(),
text: FILE_CONTENTS.to_string(),
}],
};
// distant action --format json --interactive
let cmd = action_cmd
.args(&["--format", "json"])
.arg("--interactive")
.write_stdin(format!("{}\n", serde_json::to_string(&req).unwrap()))
.assert()
.success()
.stderr("");
let res: Response = serde_json::from_slice(&cmd.get_output().stdout).unwrap();
assert!(
matches!(
res.payload[0],
ResponseData::Error(Error {
kind: ErrorKind::NotFound,
..
})
),
"Unexpected response: {:?}",
res.payload[0]
);
// Because we're talking to a local server, we can verify locally
file.assert(predicates::path::missing());
}

@ -1,2 +1,6 @@
mod file_append;
mod file_append_text;
mod file_read;
mod file_read_text;
mod file_write;
mod file_write_text;

@ -5,3 +5,8 @@ lazy_static::lazy_static! {
pub static ref FAILURE_LINE: predicates::str::RegexPredicate =
predicate::str::is_match(r"^Failed \(.*\): '.*'\.\n$").unwrap();
}
/// Creates a random tenant name
pub fn random_tenant() -> String {
format!("test-tenant-{}", rand::random::<u16>())
}

Loading…
Cancel
Save