use assert_cmd::Command;
use serde_json::Value;
use tempfile::tempdir;
fn tsafe() -> Command {
Command::cargo_bin("tsafe").unwrap()
}
fn agent_status_json(profile: &str) -> (i32, String, String) {
let dir = tempdir().unwrap();
let output = tsafe()
.args(["--profile", profile, "agent", "status", "--json"])
.env("TSAFE_VAULT_DIR", dir.path())
.env_remove("TSAFE_AGENT_SOCK")
.env_remove("TSAFE_PROFILE")
.output()
.unwrap();
(
output.status.code().unwrap_or(-1),
String::from_utf8_lossy(&output.stdout).into_owned(),
String::from_utf8_lossy(&output.stderr).into_owned(),
)
}
#[test]
#[cfg(feature = "agent")]
fn agent_status_json_exits_zero_when_no_agent_running() {
let (code, stdout, stderr) = agent_status_json("default");
assert_eq!(
code, 0,
"agent status --json must exit 0 even when no agent is running\nstdout: {stdout}\nstderr: {stderr}"
);
}
#[test]
#[cfg(feature = "agent")]
fn agent_status_json_output_is_valid_json() {
let (_, stdout, _) = agent_status_json("default");
let parsed: Result<Value, _> = serde_json::from_str(&stdout);
assert!(
parsed.is_ok(),
"agent status --json must produce valid JSON\nraw output:\n{stdout}"
);
}
#[test]
#[cfg(feature = "agent")]
fn agent_status_json_version_field_is_string_one() {
let (_, stdout, _) = agent_status_json("default");
let json: Value = serde_json::from_str(&stdout).expect("valid JSON");
assert_eq!(
json["version"],
Value::String("1".to_string()),
"`version` must be the string \"1\" in schema version 1\nfull output:\n{stdout}"
);
}
#[test]
#[cfg(feature = "agent")]
fn agent_status_json_agent_running_is_bool() {
let (_, stdout, _) = agent_status_json("default");
let json: Value = serde_json::from_str(&stdout).expect("valid JSON");
assert!(
json["agent_running"].is_boolean(),
"`agent_running` must be a boolean\nfull output:\n{stdout}"
);
}
#[test]
#[cfg(feature = "agent")]
fn agent_status_json_vault_locked_is_bool() {
let (_, stdout, _) = agent_status_json("default");
let json: Value = serde_json::from_str(&stdout).expect("valid JSON");
assert!(
json["vault_locked"].is_boolean(),
"`vault_locked` must be a boolean\nfull output:\n{stdout}"
);
}
#[test]
#[cfg(feature = "agent")]
fn agent_status_json_biometric_enabled_is_bool() {
let (_, stdout, _) = agent_status_json("default");
let json: Value = serde_json::from_str(&stdout).expect("valid JSON");
assert!(
json["biometric_enabled"].is_boolean(),
"`biometric_enabled` must be a boolean\nfull output:\n{stdout}"
);
}
#[test]
#[cfg(feature = "agent")]
fn agent_status_json_active_profile_is_string() {
let (_, stdout, _) = agent_status_json("default");
let json: Value = serde_json::from_str(&stdout).expect("valid JSON");
assert!(
json["active_profile"].is_string(),
"`active_profile` must be a string\nfull output:\n{stdout}"
);
}
#[test]
#[cfg(feature = "agent")]
fn agent_status_json_active_profile_reflects_requested_profile() {
let (_, stdout, _) = agent_status_json("myprod");
let json: Value = serde_json::from_str(&stdout).expect("valid JSON");
assert_eq!(
json["active_profile"].as_str(),
Some("myprod"),
"`active_profile` must reflect the --profile argument\nfull output:\n{stdout}"
);
}
#[test]
#[cfg(feature = "agent")]
fn agent_status_json_no_agent_reports_agent_running_false() {
let (_, stdout, _) = agent_status_json("default");
let json: Value = serde_json::from_str(&stdout).expect("valid JSON");
assert_eq!(
json["agent_running"],
Value::Bool(false),
"`agent_running` must be false when no agent socket is set\nfull output:\n{stdout}"
);
}
#[test]
#[cfg(feature = "agent")]
fn agent_status_json_no_agent_reports_vault_locked_true() {
let (_, stdout, _) = agent_status_json("default");
let json: Value = serde_json::from_str(&stdout).expect("valid JSON");
assert_eq!(
json["vault_locked"],
Value::Bool(true),
"`vault_locked` must be true when no agent is running\nfull output:\n{stdout}"
);
}
#[test]
#[cfg(feature = "agent")]
fn agent_status_json_session_expires_at_is_null_or_string() {
let (_, stdout, _) = agent_status_json("default");
let json: Value = serde_json::from_str(&stdout).expect("valid JSON");
let field = &json["session_expires_at"];
assert!(
field.is_null() || field.is_string(),
"`session_expires_at` must be null or an ISO-8601 string\nfull output:\n{stdout}"
);
}
#[test]
#[cfg(feature = "agent")]
fn agent_status_json_idle_ttl_remaining_secs_is_null_or_u64() {
let (_, stdout, _) = agent_status_json("default");
let json: Value = serde_json::from_str(&stdout).expect("valid JSON");
let field = &json["idle_ttl_remaining_secs"];
assert!(
field.is_null() || field.is_u64(),
"`idle_ttl_remaining_secs` must be null or a non-negative integer\nfull output:\n{stdout}"
);
}
#[test]
#[cfg(feature = "agent")]
fn agent_status_json_absolute_ttl_remaining_secs_is_null_or_u64() {
let (_, stdout, _) = agent_status_json("default");
let json: Value = serde_json::from_str(&stdout).expect("valid JSON");
let field = &json["absolute_ttl_remaining_secs"];
assert!(
field.is_null() || field.is_u64(),
"`absolute_ttl_remaining_secs` must be null or a non-negative integer\nfull output:\n{stdout}"
);
}
#[test]
#[cfg(feature = "agent")]
fn agent_status_json_agent_pid_is_null_or_u64() {
let (_, stdout, _) = agent_status_json("default");
let json: Value = serde_json::from_str(&stdout).expect("valid JSON");
let field = &json["agent_pid"];
assert!(
field.is_null() || field.is_u64(),
"`agent_pid` must be null or a non-negative integer\nfull output:\n{stdout}"
);
}
#[test]
#[cfg(feature = "agent")]
fn agent_status_json_v1_session_expires_at_is_null() {
let (_, stdout, _) = agent_status_json("default");
let json: Value = serde_json::from_str(&stdout).expect("valid JSON");
assert!(
json["session_expires_at"].is_null(),
"`session_expires_at` must be null in schema version 1 (requires future Status IPC variant)\nfull output:\n{stdout}"
);
}
#[test]
#[cfg(feature = "agent")]
fn agent_status_json_v1_idle_ttl_remaining_secs_is_null() {
let (_, stdout, _) = agent_status_json("default");
let json: Value = serde_json::from_str(&stdout).expect("valid JSON");
assert!(
json["idle_ttl_remaining_secs"].is_null(),
"`idle_ttl_remaining_secs` must be null in schema version 1\nfull output:\n{stdout}"
);
}
#[test]
#[cfg(feature = "agent")]
fn agent_status_json_v1_absolute_ttl_remaining_secs_is_null() {
let (_, stdout, _) = agent_status_json("default");
let json: Value = serde_json::from_str(&stdout).expect("valid JSON");
assert!(
json["absolute_ttl_remaining_secs"].is_null(),
"`absolute_ttl_remaining_secs` must be null in schema version 1\nfull output:\n{stdout}"
);
}
#[test]
#[cfg(feature = "agent")]
fn agent_status_json_all_required_fields_present() {
let (_, stdout, _) = agent_status_json("default");
let json: Value = serde_json::from_str(&stdout).expect("valid JSON");
let obj = json.as_object().expect("root must be a JSON object");
let required_fields = [
"version",
"agent_running",
"vault_locked",
"active_profile",
"biometric_enabled",
"session_expires_at",
"idle_ttl_remaining_secs",
"absolute_ttl_remaining_secs",
"agent_pid",
];
for field in required_fields {
assert!(
obj.contains_key(field),
"required field `{field}` is missing from agent status JSON\nfull output:\n{stdout}"
);
}
}
#[test]
#[cfg(feature = "agent")]
fn agent_status_human_output_is_not_json_when_json_flag_absent() {
let dir = tempdir().unwrap();
let output = tsafe()
.args(["--profile", "default", "agent", "status"])
.env("TSAFE_VAULT_DIR", dir.path())
.env_remove("TSAFE_AGENT_SOCK")
.env_remove("TSAFE_PROFILE")
.output()
.unwrap();
let stdout = String::from_utf8_lossy(&output.stdout).into_owned();
assert!(
output.status.success(),
"agent status (human mode) should exit 0\nstdout:\n{stdout}"
);
let parsed: Result<Value, _> = serde_json::from_str(&stdout);
assert!(
parsed.is_err(),
"human-readable agent status should not produce valid JSON\nstdout:\n{stdout}"
);
assert!(
stdout.contains("No agent running")
|| stdout.contains("Agent socket")
|| stdout.contains("Agent unreachable"),
"human-readable agent status should describe agent reachability\nstdout:\n{stdout}"
);
}