use std::path::PathBuf;
use std::process::Command;
fn binary() -> PathBuf {
env!("CARGO_BIN_EXE_socket-patch").into()
}
fn make_socket_with_empty_manifest(root: &std::path::Path) {
let socket = root.join(".socket");
std::fs::create_dir_all(&socket).unwrap();
std::fs::write(
socket.join("manifest.json"),
r#"{"patches":{}}"#,
)
.unwrap();
std::fs::create_dir_all(socket.join("blobs")).unwrap();
}
#[test]
fn apply_dry_run_empty_manifest_emits_dry_run_envelope() {
let tmp = tempfile::tempdir().expect("tempdir");
make_socket_with_empty_manifest(tmp.path());
let out = Command::new(binary())
.args(["apply", "--json", "--dry-run"])
.current_dir(tmp.path())
.env_remove("SOCKET_API_TOKEN")
.output()
.expect("run apply");
let stdout = String::from_utf8_lossy(&out.stdout);
let v: serde_json::Value = serde_json::from_str(stdout.trim())
.unwrap_or_else(|e| panic!("invalid JSON: {e}\n{stdout}"));
assert_eq!(v["command"], "apply");
assert_eq!(v["dryRun"], true);
}
#[test]
fn repair_dry_run_offline_emits_dry_run_envelope() {
let tmp = tempfile::tempdir().expect("tempdir");
make_socket_with_empty_manifest(tmp.path());
let out = Command::new(binary())
.args(["repair", "--json", "--dry-run", "--offline"])
.current_dir(tmp.path())
.env_remove("SOCKET_API_TOKEN")
.output()
.expect("run repair");
let stdout = String::from_utf8_lossy(&out.stdout);
let v: serde_json::Value = serde_json::from_str(stdout.trim())
.unwrap_or_else(|e| panic!("invalid JSON: {e}\n{stdout}"));
assert_eq!(v["command"], "repair");
assert_eq!(v["dryRun"], true);
}
#[test]
fn rollback_with_empty_manifest_emits_envelope() {
let tmp = tempfile::tempdir().expect("tempdir");
make_socket_with_empty_manifest(tmp.path());
let out = Command::new(binary())
.args(["rollback", "--json", "--offline"])
.current_dir(tmp.path())
.env_remove("SOCKET_API_TOKEN")
.output()
.expect("run rollback");
let stdout = String::from_utf8_lossy(&out.stdout);
let _: serde_json::Value = serde_json::from_str(stdout.trim())
.unwrap_or_else(|e| panic!("invalid JSON: {e}\nstdout:\n{stdout}\nstderr:\n{}",
String::from_utf8_lossy(&out.stderr)));
}
#[test]
fn remove_with_no_socket_dir_emits_manifest_not_found() {
let tmp = tempfile::tempdir().expect("tempdir");
let out = Command::new(binary())
.args([
"remove",
"11111111-1111-4111-8111-111111111111",
"--json",
"--yes",
"--skip-rollback",
])
.current_dir(tmp.path())
.env_remove("SOCKET_API_TOKEN")
.output()
.expect("run remove");
let stdout = String::from_utf8_lossy(&out.stdout);
let v: serde_json::Value = serde_json::from_str(stdout.trim()).expect("valid JSON");
assert_eq!(v["command"], "remove");
let code = v["error"]["code"].as_str().unwrap_or("");
assert!(
code == "manifest_not_found" || code == "not_found",
"expected manifest_not_found error; got {v}"
);
}
#[test]
fn list_with_empty_manifest_emits_empty_envelope() {
let tmp = tempfile::tempdir().expect("tempdir");
make_socket_with_empty_manifest(tmp.path());
let out = Command::new(binary())
.args(["list", "--json"])
.current_dir(tmp.path())
.env_remove("SOCKET_API_TOKEN")
.output()
.expect("run list");
let stdout = String::from_utf8_lossy(&out.stdout);
let v: serde_json::Value = serde_json::from_str(stdout.trim())
.unwrap_or_else(|e| panic!("invalid JSON: {e}\n{stdout}"));
assert_eq!(v["command"], "list");
assert_eq!(v["status"], "success");
}
#[test]
fn apply_silent_no_manifest_produces_no_output() {
let tmp = tempfile::tempdir().expect("tempdir");
let out = Command::new(binary())
.args(["apply", "--silent"])
.current_dir(tmp.path())
.env_remove("SOCKET_API_TOKEN")
.output()
.expect("run apply");
assert_eq!(out.status.code(), Some(0));
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(stdout.trim().is_empty(), "silent mode should produce no stdout");
}