Skip to content

Commit edc6803

Browse files
committed
test(cli): batch --dry-run + empty-manifest path coverage
Six new tests in cli_dry_run_paths_e2e.rs covering --dry-run flag propagation and empty-manifest early-return envelopes: apply, repair, rollback, remove, list. Plus apply --silent suppresses friendly message check. Assisted-by: Claude Code:claude-opus-4-7
1 parent e39b95b commit edc6803

1 file changed

Lines changed: 144 additions & 0 deletions

File tree

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
//! Coverage for the `--dry-run` paths across multiple commands.
2+
//! Each test runs a command with `--dry-run` against a fixture and
3+
//! asserts the JSON envelope's `dryRun: true` field — covering the
4+
//! dry-run flag-propagation branches each command's `run` has.
5+
6+
use std::path::PathBuf;
7+
use std::process::Command;
8+
9+
fn binary() -> PathBuf {
10+
env!("CARGO_BIN_EXE_socket-patch").into()
11+
}
12+
13+
fn make_socket_with_empty_manifest(root: &std::path::Path) {
14+
let socket = root.join(".socket");
15+
std::fs::create_dir_all(&socket).unwrap();
16+
std::fs::write(
17+
socket.join("manifest.json"),
18+
r#"{"patches":{}}"#,
19+
)
20+
.unwrap();
21+
std::fs::create_dir_all(socket.join("blobs")).unwrap();
22+
}
23+
24+
/// `apply --dry-run --json` against an empty manifest reports
25+
/// dryRun:true and success. Covers the dry-run flag propagation
26+
/// in `commands::apply::run`.
27+
#[test]
28+
fn apply_dry_run_empty_manifest_emits_dry_run_envelope() {
29+
let tmp = tempfile::tempdir().expect("tempdir");
30+
make_socket_with_empty_manifest(tmp.path());
31+
let out = Command::new(binary())
32+
.args(["apply", "--json", "--dry-run"])
33+
.current_dir(tmp.path())
34+
.env_remove("SOCKET_API_TOKEN")
35+
.output()
36+
.expect("run apply");
37+
let stdout = String::from_utf8_lossy(&out.stdout);
38+
let v: serde_json::Value = serde_json::from_str(stdout.trim())
39+
.unwrap_or_else(|e| panic!("invalid JSON: {e}\n{stdout}"));
40+
assert_eq!(v["command"], "apply");
41+
assert_eq!(v["dryRun"], true);
42+
}
43+
44+
/// `repair --dry-run --offline --json`: dry-run with no patches
45+
/// should succeed with `dryRun:true`.
46+
#[test]
47+
fn repair_dry_run_offline_emits_dry_run_envelope() {
48+
let tmp = tempfile::tempdir().expect("tempdir");
49+
make_socket_with_empty_manifest(tmp.path());
50+
let out = Command::new(binary())
51+
.args(["repair", "--json", "--dry-run", "--offline"])
52+
.current_dir(tmp.path())
53+
.env_remove("SOCKET_API_TOKEN")
54+
.output()
55+
.expect("run repair");
56+
let stdout = String::from_utf8_lossy(&out.stdout);
57+
let v: serde_json::Value = serde_json::from_str(stdout.trim())
58+
.unwrap_or_else(|e| panic!("invalid JSON: {e}\n{stdout}"));
59+
assert_eq!(v["command"], "repair");
60+
assert_eq!(v["dryRun"], true);
61+
}
62+
63+
/// Rollback with no patches in manifest + --json must not crash.
64+
/// Locks in the manifest-empty-but-valid branch.
65+
#[test]
66+
fn rollback_with_empty_manifest_emits_envelope() {
67+
let tmp = tempfile::tempdir().expect("tempdir");
68+
make_socket_with_empty_manifest(tmp.path());
69+
let out = Command::new(binary())
70+
.args(["rollback", "--json", "--offline"])
71+
.current_dir(tmp.path())
72+
.env_remove("SOCKET_API_TOKEN")
73+
.output()
74+
.expect("run rollback");
75+
let stdout = String::from_utf8_lossy(&out.stdout);
76+
// Should produce SOME envelope JSON without panicking.
77+
let _: serde_json::Value = serde_json::from_str(stdout.trim())
78+
.unwrap_or_else(|e| panic!("invalid JSON: {e}\nstdout:\n{stdout}\nstderr:\n{}",
79+
String::from_utf8_lossy(&out.stderr)));
80+
}
81+
82+
/// `remove --json` with no manifest at all: the early-exit
83+
/// envelope branch with `manifest_not_found` error code. Covered
84+
/// elsewhere too but a redundant lock is cheap.
85+
#[test]
86+
fn remove_with_no_socket_dir_emits_manifest_not_found() {
87+
let tmp = tempfile::tempdir().expect("tempdir");
88+
// NO .socket/ directory at all.
89+
let out = Command::new(binary())
90+
.args([
91+
"remove",
92+
"11111111-1111-4111-8111-111111111111",
93+
"--json",
94+
"--yes",
95+
"--skip-rollback",
96+
])
97+
.current_dir(tmp.path())
98+
.env_remove("SOCKET_API_TOKEN")
99+
.output()
100+
.expect("run remove");
101+
let stdout = String::from_utf8_lossy(&out.stdout);
102+
let v: serde_json::Value = serde_json::from_str(stdout.trim()).expect("valid JSON");
103+
assert_eq!(v["command"], "remove");
104+
let code = v["error"]["code"].as_str().unwrap_or("");
105+
assert!(
106+
code == "manifest_not_found" || code == "not_found",
107+
"expected manifest_not_found error; got {v}"
108+
);
109+
}
110+
111+
/// `list --json` against an empty manifest emits an empty
112+
/// `patches` array and status=success. Covers the list-empty path.
113+
#[test]
114+
fn list_with_empty_manifest_emits_empty_envelope() {
115+
let tmp = tempfile::tempdir().expect("tempdir");
116+
make_socket_with_empty_manifest(tmp.path());
117+
let out = Command::new(binary())
118+
.args(["list", "--json"])
119+
.current_dir(tmp.path())
120+
.env_remove("SOCKET_API_TOKEN")
121+
.output()
122+
.expect("run list");
123+
let stdout = String::from_utf8_lossy(&out.stdout);
124+
let v: serde_json::Value = serde_json::from_str(stdout.trim())
125+
.unwrap_or_else(|e| panic!("invalid JSON: {e}\n{stdout}"));
126+
assert_eq!(v["command"], "list");
127+
assert_eq!(v["status"], "success");
128+
}
129+
130+
/// `--silent` flag suppresses the friendly "no manifest" message
131+
/// in non-JSON mode for `apply`. Covers the silent-flag short-circuit.
132+
#[test]
133+
fn apply_silent_no_manifest_produces_no_output() {
134+
let tmp = tempfile::tempdir().expect("tempdir");
135+
let out = Command::new(binary())
136+
.args(["apply", "--silent"])
137+
.current_dir(tmp.path())
138+
.env_remove("SOCKET_API_TOKEN")
139+
.output()
140+
.expect("run apply");
141+
assert_eq!(out.status.code(), Some(0));
142+
let stdout = String::from_utf8_lossy(&out.stdout);
143+
assert!(stdout.trim().is_empty(), "silent mode should produce no stdout");
144+
}

0 commit comments

Comments
 (0)