use super::*;
#[tokio::test]
async fn a_version_difference_with_no_protocol_difference_is_refused() {
let dir = tempfile::tempdir().unwrap();
let addr = shep_client::testing::control_address(dir.path());
let (client, _fake) = client_announcing(&addr, "0.1.8").await;
let mut out = Vec::new();
let mut err = Vec::new();
let mut streams = buffered_streams(&mut out, &mut err);
let code = refuse_version_skew(&mut streams, &client, VersionGuard::Enforce)
.expect_err("a differing crate version must be refused");
assert_eq!(code, ExitCode::VersionSkew);
}
#[tokio::test]
async fn the_error_names_the_command_that_fixes_it() {
let dir = tempfile::tempdir().unwrap();
let addr = shep_client::testing::control_address(dir.path());
let (client, _fake) = client_announcing(&addr, "0.1.8").await;
let mut out = Vec::new();
let mut err = Vec::new();
{
let mut streams = buffered_streams(&mut out, &mut err);
let _ = refuse_version_skew(&mut streams, &client, VersionGuard::Enforce);
}
let text = String::from_utf8(err).unwrap();
assert!(text.contains("error[version_skew]"), "{text}");
assert!(text.contains("this shep is"), "{text}");
assert!(text.contains(env!("CARGO_PKG_VERSION")), "{text}");
assert!(text.contains("the running shepherd is 0.1.8"), "{text}");
assert!(
text.contains("`cargo install shep` replaced the binary"),
"{text}"
);
assert!(text.contains("shep daemon reload"), "{text}");
}
#[tokio::test]
async fn the_table_form_names_the_remedy_as_an_instruction_not_only_a_line() {
let dir = tempfile::tempdir().unwrap();
let addr = shep_client::testing::control_address(dir.path());
let (client, _fake) = client_announcing(&addr, "0.1.8").await;
let mut out = Vec::new();
let mut err = Vec::new();
{
let mut streams = buffered_streams(&mut out, &mut err);
let _ = refuse_version_skew(&mut streams, &client, VersionGuard::Enforce);
}
let text = String::from_utf8(err).unwrap();
assert!(
text.contains("Run:\n shep daemon reload"),
"the label must sit directly on the copyable line it points at: {text}"
);
assert_eq!(
text.matches("shep daemon reload").count(),
1,
"the remedy is named once, not restated in prose: {text}"
);
}
#[tokio::test]
async fn flock_reports_a_refusal_as_a_refusal_not_as_no_daemon() {
let dir = tempfile::tempdir().unwrap();
let paths = ShepPaths::resolve(&|_| None, dir.path());
std::fs::create_dir_all(&paths.run).unwrap();
let refusal = shep_core::protocol::RpcError {
code: shep_core::protocol::RpcErrorCode::ProtocolMismatch,
message: "this daemon speaks protocol 1, this client speaks 2".to_string(),
daemon_version: Some("0.1.8".to_string()),
};
let _daemon = shep_client::testing::fake_daemon(&paths.socket, Err(refusal)).await;
let mut out = Vec::new();
let mut err = Vec::new();
let code = {
let mut streams = buffered_streams(&mut out, &mut err);
flock_command(
&mut streams,
&Link::Local(&paths),
VersionGuard::Enforce,
&flock_args(),
)
.await
};
assert_ne!(code, ExitCode::Success);
let text = String::from_utf8(err).unwrap();
assert!(
!text.contains("no shepherd running"),
"a refusal is not an absence: {text}"
);
assert!(text.contains("shep daemon reload"), "{text}");
}
#[tokio::test]
async fn flock_json_reports_the_refusal_it_got_not_a_second_connect_failure() {
let dir = tempfile::tempdir().unwrap();
let paths = ShepPaths::resolve(&|_| None, dir.path());
std::fs::create_dir_all(&paths.run).unwrap();
let refusal = shep_core::protocol::RpcError {
code: shep_core::protocol::RpcErrorCode::ProtocolMismatch,
message: "this daemon speaks protocol 1, this client speaks 2".to_string(),
daemon_version: Some("0.1.8".to_string()),
};
let daemon = shep_client::testing::fake_daemon(&paths.socket, Err(refusal)).await;
let mut out = Vec::new();
let mut err = Vec::new();
let code = {
let mut streams = buffered_streams(&mut out, &mut err);
streams.fmt = Format::Json;
flock_command(
&mut streams,
&Link::Local(&paths),
VersionGuard::Enforce,
&flock_args(),
)
.await
};
daemon.await.unwrap();
assert_eq!(code, ExitCode::ProtocolMismatch);
let text = String::from_utf8(err).unwrap();
assert!(text.contains("this daemon speaks protocol 1"), "{text}");
assert!(out.is_empty(), "{}", String::from_utf8_lossy(&out));
}
#[tokio::test]
async fn flock_still_falls_back_to_the_roll_for_a_genuine_absence() {
let dir = tempfile::tempdir().unwrap();
let paths = ShepPaths::resolve(&|_| None, dir.path());
std::fs::create_dir_all(&paths.run).unwrap();
let mut out = Vec::new();
let mut err = Vec::new();
let code = {
let mut streams = buffered_streams(&mut out, &mut err);
flock_command(
&mut streams,
&Link::Local(&paths),
VersionGuard::Enforce,
&flock_args(),
)
.await
};
assert_eq!(code, ExitCode::DaemonUnreachable);
let text = String::from_utf8(err).unwrap();
assert!(text.contains("no shepherd running"), "{text}");
}
#[tokio::test]
async fn follow_refuses_json_and_a_pipe_for_different_reasons() {
let dir = tempfile::tempdir().unwrap();
let paths = ShepPaths::resolve(&|_| None, dir.path());
let args = cli::FlockArgs {
follow: true,
interval: 1,
};
let mut out = Vec::new();
let mut err = Vec::new();
let code = {
let mut streams = buffered_streams(&mut out, &mut err);
streams.fmt = Format::Json;
flock_command(
&mut streams,
&Link::Local(&paths),
VersionGuard::Enforce,
&args,
)
.await
};
assert_eq!(code, ExitCode::Usage);
let text = String::from_utf8(err).unwrap();
assert!(
text.contains("no follow form"),
"the json guard answers before the terminal one: {text}"
);
if std::io::stdout().is_terminal() {
return;
}
let mut out = Vec::new();
let mut err = Vec::new();
let code = {
let mut streams = buffered_streams(&mut out, &mut err);
flock_command(
&mut streams,
&Link::Local(&paths),
VersionGuard::Enforce,
&args,
)
.await
};
assert_eq!(code, ExitCode::Usage);
let text = String::from_utf8(err).unwrap();
assert!(text.contains("needs a terminal"), "{text}");
assert!(
out.is_empty(),
"a refused follow prints no listing: {}",
String::from_utf8_lossy(&out)
);
}
#[tokio::test]
async fn a_matching_version_passes_without_a_word() {
let dir = tempfile::tempdir().unwrap();
let addr = shep_client::testing::control_address(dir.path());
let (client, _fake) = client_announcing(&addr, env!("CARGO_PKG_VERSION")).await;
let mut out = Vec::new();
let mut err = Vec::new();
{
let mut streams = buffered_streams(&mut out, &mut err);
refuse_version_skew(&mut streams, &client, VersionGuard::Enforce)
.expect("a matching version is not a skew");
}
assert!(err.is_empty(), "{}", String::from_utf8_lossy(&err));
}
#[tokio::test]
async fn the_recovery_verbs_are_not_refused() {
let dir = tempfile::tempdir().unwrap();
let addr = shep_client::testing::control_address(dir.path());
let (client, _fake) = client_announcing(&addr, "0.1.8").await;
let mut out = Vec::new();
let mut err = Vec::new();
{
let mut streams = buffered_streams(&mut out, &mut err);
refuse_version_skew(&mut streams, &client, VersionGuard::Exempt)
.expect("a recovery verb is never refused on version skew");
}
assert!(err.is_empty(), "{}", String::from_utf8_lossy(&err));
}