mirror of https://github.com/chipsenkbeil/distant
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
280 lines
8.4 KiB
Rust
280 lines
8.4 KiB
Rust
use crate::cli::{fixtures::*, scripts::*};
|
|
use rstest::*;
|
|
use serde_json::json;
|
|
use test_log::test;
|
|
|
|
fn make_cmd(args: Vec<&str>) -> String {
|
|
format!(
|
|
r#"{} {} {}"#,
|
|
*SCRIPT_RUNNER,
|
|
*SCRIPT_RUNNER_ARG,
|
|
args.join(" ")
|
|
)
|
|
}
|
|
|
|
fn trim(arr: &Vec<serde_json::Value>) -> &[serde_json::Value] {
|
|
let arr = arr.as_slice();
|
|
|
|
if arr.is_empty() {
|
|
return arr;
|
|
}
|
|
|
|
let mut start = 0;
|
|
let mut end = arr.len() - 1;
|
|
let mut i = start;
|
|
|
|
fn is_whitespace(value: &serde_json::Value) -> bool {
|
|
value == b' ' || value == b'\t' || value == b'\r' || value == b'\n'
|
|
}
|
|
|
|
// Trim from front
|
|
while start < end {
|
|
if is_whitespace(&arr[i]) {
|
|
start = i + 1;
|
|
i += 1;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
i = end;
|
|
|
|
// Trim from back
|
|
while end > start {
|
|
if is_whitespace(&arr[i]) {
|
|
end = i - 1;
|
|
i -= 1;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
&arr[start..=end]
|
|
}
|
|
|
|
// Trim and compare value to string
|
|
fn check_value_as_str(value: &serde_json::Value, other: &str) {
|
|
let arr = trim(value.as_array().expect("value should be a byte array"));
|
|
|
|
if arr != other.as_bytes() {
|
|
let s = arr
|
|
.iter()
|
|
.map(|value| {
|
|
(value
|
|
.as_u64()
|
|
.expect("Invalid array value, expected number") as u8) as char
|
|
})
|
|
.collect::<String>();
|
|
panic!("Expected '{other}', but got '{s}'");
|
|
}
|
|
}
|
|
|
|
#[rstest]
|
|
#[test(tokio::test)]
|
|
async fn should_support_json_to_execute_program_and_return_exit_status(
|
|
mut json_repl: CtxCommand<Repl>,
|
|
) {
|
|
validate_authentication(&mut json_repl).await;
|
|
|
|
let cmd = make_cmd(vec![ECHO_ARGS_TO_STDOUT.to_str().unwrap()]);
|
|
|
|
let id = rand::random::<u64>().to_string();
|
|
let req = json!({
|
|
"id": id,
|
|
"payload": {
|
|
"type": "proc_spawn",
|
|
"cmd": cmd,
|
|
"pty": null,
|
|
},
|
|
});
|
|
|
|
let res = json_repl.write_and_read_json(req).await.unwrap().unwrap();
|
|
|
|
assert_eq!(res["origin_id"], id, "JSON: {res}");
|
|
assert_eq!(res["payload"]["type"], "proc_spawned", "JSON: {res}");
|
|
}
|
|
|
|
#[rstest]
|
|
#[test(tokio::test)]
|
|
async fn should_support_json_to_capture_and_print_stdout(mut json_repl: CtxCommand<Repl>) {
|
|
validate_authentication(&mut json_repl).await;
|
|
|
|
let cmd = make_cmd(vec![ECHO_ARGS_TO_STDOUT.to_str().unwrap(), "some output"]);
|
|
|
|
// Spawn the process
|
|
let origin_id = rand::random::<u64>().to_string();
|
|
let req = json!({
|
|
"id": origin_id,
|
|
"payload": {
|
|
"type": "proc_spawn",
|
|
"cmd": cmd,
|
|
"pty": null,
|
|
},
|
|
});
|
|
|
|
let res = json_repl.write_and_read_json(req).await.unwrap().unwrap();
|
|
|
|
assert_eq!(res["origin_id"], origin_id, "JSON: {res}");
|
|
assert_eq!(res["payload"]["type"], "proc_spawned", "JSON: {res}");
|
|
|
|
// Wait for output to show up (for stderr)
|
|
let res = json_repl.read_json_from_stdout().await.unwrap().unwrap();
|
|
|
|
assert_eq!(res["origin_id"], origin_id, "JSON: {res}");
|
|
assert_eq!(res["payload"]["type"], "proc_stdout", "JSON: {res}");
|
|
check_value_as_str(&res["payload"]["data"], "some output");
|
|
|
|
// Now we wait for the process to complete
|
|
let res = json_repl.read_json_from_stdout().await.unwrap().unwrap();
|
|
|
|
assert_eq!(res["origin_id"], origin_id, "JSON: {res}");
|
|
assert_eq!(res["payload"]["type"], "proc_done", "JSON: {res}");
|
|
assert_eq!(res["payload"]["success"], true, "JSON: {res}");
|
|
}
|
|
|
|
#[rstest]
|
|
#[test(tokio::test)]
|
|
async fn should_support_json_to_capture_and_print_stderr(mut json_repl: CtxCommand<Repl>) {
|
|
validate_authentication(&mut json_repl).await;
|
|
|
|
let cmd = make_cmd(vec![ECHO_ARGS_TO_STDERR.to_str().unwrap(), "some output"]);
|
|
|
|
// Spawn the process
|
|
let origin_id = rand::random::<u64>().to_string();
|
|
let req = json!({
|
|
"id": origin_id,
|
|
"payload": {
|
|
"type": "proc_spawn",
|
|
"cmd": cmd,
|
|
"pty": null,
|
|
},
|
|
});
|
|
|
|
let res = json_repl.write_and_read_json(req).await.unwrap().unwrap();
|
|
|
|
assert_eq!(res["origin_id"], origin_id, "JSON: {res}");
|
|
assert_eq!(res["payload"]["type"], "proc_spawned", "JSON: {res}");
|
|
|
|
// Wait for output to show up (for stderr)
|
|
let res = json_repl.read_json_from_stdout().await.unwrap().unwrap();
|
|
|
|
assert_eq!(res["origin_id"], origin_id, "JSON: {res}");
|
|
assert_eq!(res["payload"]["type"], "proc_stderr", "JSON: {res}");
|
|
check_value_as_str(&res["payload"]["data"], "some output");
|
|
|
|
// Now we wait for the process to complete
|
|
let res = json_repl.read_json_from_stdout().await.unwrap().unwrap();
|
|
|
|
assert_eq!(res["origin_id"], origin_id, "JSON: {res}");
|
|
assert_eq!(res["payload"]["type"], "proc_done", "JSON: {res}");
|
|
assert_eq!(res["payload"]["success"], true, "JSON: {res}");
|
|
}
|
|
|
|
#[rstest]
|
|
#[test(tokio::test)]
|
|
async fn should_support_json_to_forward_stdin_to_remote_process(mut json_repl: CtxCommand<Repl>) {
|
|
validate_authentication(&mut json_repl).await;
|
|
|
|
let cmd = make_cmd(vec![ECHO_STDIN_TO_STDOUT.to_str().unwrap()]);
|
|
|
|
// Spawn the process
|
|
let origin_id = rand::random::<u64>().to_string();
|
|
let req = json!({
|
|
"id": origin_id,
|
|
"payload": {
|
|
"type": "proc_spawn",
|
|
"cmd": cmd,
|
|
"pty": null,
|
|
},
|
|
});
|
|
|
|
let res = json_repl.write_and_read_json(req).await.unwrap().unwrap();
|
|
|
|
assert_eq!(res["origin_id"], origin_id, "JSON: {res}");
|
|
assert_eq!(res["payload"]["type"], "proc_spawned", "JSON: {res}");
|
|
|
|
// Write output to stdin of process to trigger getting it back as stdout
|
|
let proc_id = res["payload"]["id"]
|
|
.as_u64()
|
|
.expect("Invalid proc id value");
|
|
let id = rand::random::<u64>().to_string();
|
|
let req = json!({
|
|
"id": id,
|
|
"payload": {
|
|
"type": "proc_stdin",
|
|
"id": proc_id,
|
|
"data": b"some output\n",
|
|
},
|
|
});
|
|
|
|
let res = json_repl.write_and_read_json(req).await.unwrap().unwrap();
|
|
|
|
assert_eq!(res["origin_id"], id, "JSON: {res}");
|
|
assert_eq!(res["payload"]["type"], "ok", "JSON: {res}");
|
|
|
|
let res = json_repl.read_json_from_stdout().await.unwrap().unwrap();
|
|
|
|
assert_eq!(res["origin_id"], origin_id, "JSON: {res}");
|
|
assert_eq!(res["payload"]["type"], "proc_stdout", "JSON: {res}");
|
|
check_value_as_str(&res["payload"]["data"], "some output");
|
|
|
|
// Now kill the process and wait for it to complete
|
|
let id = rand::random::<u64>().to_string();
|
|
let res_1 = json_repl
|
|
.write_and_read_json(json!({
|
|
"id": id,
|
|
"payload": {
|
|
"type": "proc_kill",
|
|
"id": proc_id,
|
|
},
|
|
|
|
}))
|
|
.await
|
|
.unwrap()
|
|
.unwrap();
|
|
let res_2 = json_repl.read_json_from_stdout().await.unwrap().unwrap();
|
|
|
|
// The order of responses may be different (kill could come before ok), so we need
|
|
// to check that we get one of each type
|
|
let got_ok = res_1["payload"]["type"] == "ok" || res_2["payload"]["type"] == "ok";
|
|
let got_done =
|
|
res_1["payload"]["type"] == "proc_done" || res_2["payload"]["type"] == "proc_done";
|
|
|
|
if res_1["payload"]["type"] == "ok" {
|
|
assert_eq!(res_1["origin_id"], id, "JSON: {res_1}");
|
|
} else if res_1["payload"]["type"] == "proc_done" {
|
|
assert_eq!(res_1["origin_id"], origin_id, "JSON: {res_1}");
|
|
}
|
|
|
|
if res_2["payload"]["type"] == "ok" {
|
|
assert_eq!(res_2["origin_id"], id, "JSON: {res_2}");
|
|
} else if res_2["payload"]["type"] == "proc_done" {
|
|
assert_eq!(res_2["origin_id"], origin_id, "JSON: {res_2}");
|
|
}
|
|
|
|
assert!(got_ok, "Did not receive ok from proc_kill");
|
|
assert!(got_done, "Did not receive proc_done from killed process");
|
|
}
|
|
|
|
#[rstest]
|
|
#[test(tokio::test)]
|
|
async fn should_support_json_output_for_error(mut json_repl: CtxCommand<Repl>) {
|
|
validate_authentication(&mut json_repl).await;
|
|
|
|
let id = rand::random::<u64>().to_string();
|
|
let req = json!({
|
|
"id": id,
|
|
"payload": {
|
|
"type": "proc_spawn",
|
|
"cmd": DOES_NOT_EXIST_BIN.to_str().unwrap().to_string(),
|
|
"pty": null,
|
|
},
|
|
});
|
|
|
|
let res = json_repl.write_and_read_json(req).await.unwrap().unwrap();
|
|
|
|
assert_eq!(res["origin_id"], id, "JSON: {res}");
|
|
assert_eq!(res["payload"]["type"], "error", "JSON: {res}");
|
|
assert_eq!(res["payload"]["kind"], "not_found", "JSON: {res}");
|
|
}
|