Add first example of an e2e test for distant cli

This commit is contained in:
Chip Senkbeil 2021-08-29 01:03:18 -05:00
parent 54a7f567a1
commit 188b5f74e4
No known key found for this signature in database
GPG Key ID: 35EF1F8EC72A4131
7 changed files with 267 additions and 15 deletions

112
Cargo.lock generated
View File

@ -34,6 +34,20 @@ dependencies = [
"wait-timeout", "wait-timeout",
] ]
[[package]]
name = "assert_fs"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0ca6aa3066e6c6f0357e056a25fa95e8737f15a04f9aead0b22d0d082a39465"
dependencies = [
"doc-comment",
"globwalk",
"predicates",
"predicates-core",
"predicates-tree",
"tempfile",
]
[[package]] [[package]]
name = "atty" name = "atty"
version = "0.2.14" version = "0.2.14"
@ -144,6 +158,16 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "crossbeam-utils"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db"
dependencies = [
"cfg-if",
"lazy_static",
]
[[package]] [[package]]
name = "crypto-bigint" name = "crypto-bigint"
version = "0.2.4" version = "0.2.4"
@ -213,6 +237,7 @@ name = "distant"
version = "0.13.0" version = "0.13.0"
dependencies = [ dependencies = [
"assert_cmd", "assert_cmd",
"assert_fs",
"derive_more", "derive_more",
"distant-core", "distant-core",
"flexi_logger", "flexi_logger",
@ -220,10 +245,10 @@ dependencies = [
"lazy_static", "lazy_static",
"log", "log",
"rand", "rand",
"rstest",
"serde_json", "serde_json",
"structopt", "structopt",
"strum", "strum",
"tempfile",
"tokio", "tokio",
"whoami", "whoami",
] ]
@ -318,6 +343,12 @@ dependencies = [
"yansi", "yansi",
] ]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]] [[package]]
name = "fork" name = "fork"
version = "0.1.18" version = "0.1.18"
@ -448,6 +479,30 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
[[package]]
name = "globset"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10463d9ff00a2a068db14231982f5132edebad0d7660cd956a1c30292dbcbfbd"
dependencies = [
"aho-corasick",
"bstr",
"fnv",
"log",
"regex",
]
[[package]]
name = "globwalk"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc"
dependencies = [
"bitflags",
"ignore",
"walkdir",
]
[[package]] [[package]]
name = "group" name = "group"
version = "0.10.0" version = "0.10.0"
@ -499,6 +554,24 @@ dependencies = [
"digest", "digest",
] ]
[[package]]
name = "ignore"
version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d"
dependencies = [
"crossbeam-utils",
"globset",
"lazy_static",
"log",
"memchr",
"regex",
"same-file",
"thread_local",
"walkdir",
"winapi-util",
]
[[package]] [[package]]
name = "instant" name = "instant"
version = "0.1.10" version = "0.1.10"
@ -879,6 +952,28 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "rstest"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2288c66aeafe3b2ed227c981f364f9968fa952ef0b30e84ada4486e7ee24d00a"
dependencies = [
"cfg-if",
"proc-macro2",
"quote",
"rustc_version",
"syn",
]
[[package]]
name = "rustc_version"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
dependencies = [
"semver",
]
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.5" version = "1.0.5"
@ -900,6 +995,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "semver"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.130" version = "1.0.130"
@ -1105,6 +1206,15 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "thread_local"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd"
dependencies = [
"once_cell",
]
[[package]] [[package]]
name = "time" name = "time"
version = "0.1.43" version = "0.1.43"

View File

@ -35,4 +35,5 @@ whoami = "1.1.2"
[dev-dependencies] [dev-dependencies]
assert_cmd = "2.0.0" assert_cmd = "2.0.0"
tempfile = "3.2.0" assert_fs = "1.0.3"
rstest = "0.11.0"

View File

@ -26,23 +26,28 @@ pub fn run() {
fn init_logging(opt: &opt::CommonOpt) -> flexi_logger::LoggerHandle { fn init_logging(opt: &opt::CommonOpt) -> flexi_logger::LoggerHandle {
use flexi_logger::{FileSpec, LevelFilter, LogSpecification, Logger}; use flexi_logger::{FileSpec, LevelFilter, LogSpecification, Logger};
let module = "distant"; let modules = &["distant", "distant_core"];
// Disable logging for everything but our binary, which is based on verbosity // Disable logging for everything but our binary, which is based on verbosity
let mut builder = LogSpecification::builder(); let mut builder = LogSpecification::builder();
builder.default(LevelFilter::Off).module( builder.default(LevelFilter::Off);
module,
match opt.verbose {
0 => LevelFilter::Warn,
1 => LevelFilter::Info,
2 => LevelFilter::Debug,
_ => LevelFilter::Trace,
},
);
// If quiet, we suppress all output // For each module, configure logging
if opt.quiet { for module in modules {
builder.module(module, LevelFilter::Off); builder.module(
module,
match opt.verbose {
0 => LevelFilter::Warn,
1 => LevelFilter::Info,
2 => LevelFilter::Debug,
_ => LevelFilter::Trace,
},
);
// If quiet, we suppress all output
if opt.quiet {
builder.module(module, LevelFilter::Off);
}
} }
// Create our logger, but don't initialize yet // Create our logger, but don't initialize yet

View File

@ -0,0 +1,77 @@
use crate::fixtures::*;
use assert_fs::prelude::*;
use distant_core::{
data::{Error, ErrorKind},
Response, ResponseData,
};
use rstest::*;
#[rstest]
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn should_print_out_file_contents(#[future] ctx: DistantServerCtx) {
let temp = assert_fs::TempDir::new().unwrap();
let file = temp.child("test-file");
file.write_str("some\ntext\ncontent").unwrap();
ctx.await
.new_cmd("action")
.args(&["file-read", file.to_str().unwrap()])
.assert()
.success()
.stdout("some\ntext\ncontent\n")
.stderr("");
}
#[rstest]
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn should_support_json_output(#[future] ctx: DistantServerCtx) {
let temp = assert_fs::TempDir::new().unwrap();
let file = temp.child("test-file");
file.write_str("some\ntext\ncontent").unwrap();
let cmd = ctx
.await
.new_cmd("action")
.args(&["--format", "json"])
.args(&["file-read", file.to_str().unwrap()])
.assert()
.success()
.stderr("");
let res: Response = serde_json::from_slice(&cmd.get_output().stdout).unwrap();
assert_eq!(
res.payload[0],
ResponseData::Blob {
data: b"some\ntext\ncontent".to_vec()
}
);
}
#[rstest]
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn yield_an_error_when_fails(#[future] ctx: DistantServerCtx) {
let temp = assert_fs::TempDir::new().unwrap();
let file = temp.child("missing-file");
let cmd = ctx
.await
.new_cmd("action")
.args(&["--format", "json"])
.args(&["file-read", file.to_str().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]
);
}

1
tests/action/mod.rs Normal file
View File

@ -0,0 +1 @@
mod file_read_test;

2
tests/e2e_tests.rs Normal file
View File

@ -0,0 +1,2 @@
mod action;
mod fixtures;

56
tests/fixtures.rs Normal file
View File

@ -0,0 +1,56 @@
use assert_cmd::Command;
use distant_core::*;
use rstest::*;
use std::{ffi::OsStr, net::SocketAddr, time::Duration};
/// Timeout to wait for a command to complete
const TIMEOUT_SECS: u64 = 10;
/// Context for some listening distant server
pub struct DistantServerCtx {
pub addr: SocketAddr,
pub auth_key: String,
pub server: DistantServer,
}
impl DistantServerCtx {
/// Produces a new test command that configures some distant command
/// configured with an environment that can talk to a remote distant server
pub fn new_cmd(&self, subcommand: impl AsRef<OsStr>) -> Command {
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME")).unwrap();
println!("DISTANT_HOST = {}", self.addr.ip());
println!("DISTANT_PORT = {}", self.addr.port());
println!("DISTANT_AUTH_KEY = {}", self.auth_key);
// NOTE: We define a command that has a timeout of 10s because the handshake
// involved in a non-release test can take several seconds
cmd.arg(subcommand)
.args(&["--session", "environment"])
.env("DISTANT_HOST", self.addr.ip().to_string())
.env("DISTANT_PORT", self.addr.port().to_string())
.env("DISTANT_AUTH_KEY", self.auth_key.as_str())
.timeout(Duration::from_secs(TIMEOUT_SECS));
cmd
}
}
impl Drop for DistantServerCtx {
fn drop(&mut self) {
self.server.abort();
}
}
#[fixture]
pub async fn ctx() -> DistantServerCtx {
let ip_addr = "127.0.0.1".parse().unwrap();
let server = DistantServer::bind(ip_addr, "0".parse().unwrap(), None, 100)
.await
.unwrap();
DistantServerCtx {
addr: SocketAddr::new(ip_addr, server.port()),
auth_key: server.to_unprotected_hex_auth_key(),
server,
}
}