Skip to content

Commit 28eed29

Browse files
test(cli): parse suites onto hermetic::try_parse (T56) (#1978)
* Start T56: parse suites onto hermetic::try_parse Assisted-by: Claude Code:claude-opus-5-5 * Parse suites onto hermetic::try_parse The cli_parse_* suites and cli_global_args kept four ways to keep ambient SOCKET_* vars out of a parse: four copies of EnvScrub with drifted hand lists, scan's with_clean_env, global_args' save/restore, and nothing at all in list, remove and main (SOCKET_JSON=true failed them). Every parse now goes through hermetic::try_parse, which drops every SOCKET_* var once per process. Tests that set a var use the new common/clap_parse.rs try_parse_with_env and stay #[serial]; get, list, remove and main need no #[serial] at all. The four hand Snap mirrors of GlobalArgs (get and repair missed four fields, vendor nine, vex one) are replaced by one hand-written GlobalArgs literal compared through Debug, so a new global field fails to compile instead of going unchecked. The scrub self-tests, which only tested the deleted lists, are gone. Fixes #1956 Assisted-by: Claude Code:claude-opus-5-5 * Keep forced opt-outs in list and remove parses cli_parse_list and cli_parse_remove call list::run and remove::run in-process on parallel threads. The once-per-process SOCKET_* scrub removed SOCKET_TELEMETRY_DISABLED and SOCKET_NO_CONFIG under them, so those runs could read a developer's socket login and send telemetry. Their parses now go through try_parse_keeping_opt_outs, which keeps and forces hermetic::FORCED_OPT_OUTS on; their default oracle expects no_telemetry true accordingly. cli_global_args' last hand set_var/remove_var pairs move onto try_parse_with_env, so a failed assert no longer leaks a var into later tests, and scan's defaults test compares the whole GlobalArgs against the shared oracle. Assisted-by: Claude Code:claude-opus-5-5 --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 7ee8452 commit 28eed29

10 files changed

Lines changed: 418 additions & 1471 deletions

File tree

‎crates/socket-patch-cli/tests/cli_global_args.rs‎

Lines changed: 47 additions & 204 deletions
Large diffs are not rendered by default.

‎crates/socket-patch-cli/tests/cli_parse_get.rs‎

Lines changed: 37 additions & 290 deletions
Large diffs are not rendered by default.

‎crates/socket-patch-cli/tests/cli_parse_list.rs‎

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,16 @@
1313
use std::collections::HashMap;
1414
use std::path::{Path, PathBuf};
1515

16-
use clap::Parser;
1716
use socket_patch_cli::commands::list::{run, ListArgs};
1817

18+
#[path = "common/clap_parse.rs"]
19+
mod clap_parse;
1920
#[path = "common/mod.rs"]
2021
mod common;
21-
use common::hermetic::binary_command;
22-
use socket_patch_cli::{Cli, Commands};
22+
use clap_parse::try_parse_keeping_opt_outs as try_parse;
23+
use common::hermetic;
24+
use hermetic::binary_command;
25+
use socket_patch_cli::Commands;
2326
use socket_patch_core::manifest::schema::{
2427
PatchFileInfo, PatchManifest, PatchRecord, VulnerabilityInfo,
2528
};
@@ -31,7 +34,7 @@ use socket_patch_core::manifest::schema::{
3134
fn parse_list(extra: &[&str]) -> ListArgs {
3235
let mut argv = vec!["socket-patch", "list"];
3336
argv.extend_from_slice(extra);
34-
let cli = Cli::try_parse_from(&argv).expect("parse");
37+
let cli = try_parse(&argv).expect("parse");
3538
match cli.command {
3639
Commands::List(a) => a,
3740
_ => panic!("expected List"),
@@ -44,10 +47,12 @@ fn parse_list(extra: &[&str]) -> ListArgs {
4447

4548
#[test]
4649
fn defaults_match_contract() {
50+
// The `run()` tests below need the forced opt-outs, so parses here see
51+
// `SOCKET_TELEMETRY_DISABLED=1`.
4752
let args = parse_list(&[]);
48-
assert_eq!(args.common.cwd, PathBuf::from("."));
49-
assert_eq!(args.common.manifest_path, ".socket/manifest.json");
50-
assert!(!args.common.json);
53+
let mut want = clap_parse::global_defaults();
54+
want.no_telemetry = true;
55+
assert_eq!(clap_parse::Globals(args.common), want);
5156
}
5257

5358
#[test]
@@ -70,7 +75,7 @@ fn json_flag_sets_true() {
7075

7176
#[test]
7277
fn unknown_flag_is_rejected() {
73-
let err = match Cli::try_parse_from(["socket-patch", "list", "--nope"]) {
78+
let err = match try_parse(&["socket-patch", "list", "--nope"]) {
7479
Ok(_) => panic!("unknown flag must fail"),
7580
Err(e) => e,
7681
};

‎crates/socket-patch-cli/tests/cli_parse_main.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ mod hermetic;
2323
/// the real CLI. Routing through the wrapper means each name/alias/error-kind
2424
/// assertion below also exercises the code path users actually hit.
2525
fn parse(argv: &[&str]) -> Result<Cli, clap::Error> {
26+
hermetic::scrub_process_socket_env();
2627
parse_argv_with_shortcuts(argv.iter().map(|s| s.to_string()).collect())
2728
}
2829

‎crates/socket-patch-cli/tests/cli_parse_remove.rs‎

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,36 @@
77
//! These tests deliberately avoid spawning the binary so they run in the
88
//! default `cargo test` set (no `--ignored` required) and stay fast.
99
10-
use clap::Parser;
1110
use socket_patch_cli::commands::remove::{run, RemoveArgs};
12-
use socket_patch_cli::{Cli, Commands};
11+
use socket_patch_cli::Commands;
1312
use std::path::PathBuf;
1413

14+
#[path = "common/clap_parse.rs"]
15+
mod clap_parse;
1516
#[path = "common/hermetic.rs"]
1617
mod hermetic;
1718

19+
use clap_parse::try_parse_keeping_opt_outs as try_parse;
20+
use clap_parse::{global_defaults, Globals};
21+
1822
fn parse_remove(extra: &[&str]) -> RemoveArgs {
1923
let mut argv = vec!["socket-patch", "remove"];
2024
argv.extend_from_slice(extra);
21-
let cli = Cli::try_parse_from(&argv).expect("parse");
25+
let cli = try_parse(&argv).expect("parse");
2226
match cli.command {
2327
Commands::Remove(a) => a,
2428
_ => panic!("expected Remove"),
2529
}
2630
}
2731

32+
/// The `run()` tests below need the forced opt-outs, so parses here see
33+
/// `SOCKET_TELEMETRY_DISABLED=1`.
34+
fn opt_out_defaults() -> Globals {
35+
let mut want = global_defaults();
36+
want.no_telemetry = true;
37+
want
38+
}
39+
2840
// ---------------------------------------------------------------------------
2941
// Defaults
3042
// ---------------------------------------------------------------------------
@@ -33,13 +45,8 @@ fn parse_remove(extra: &[&str]) -> RemoveArgs {
3345
fn defaults_with_purl_positional() {
3446
let args = parse_remove(&["pkg:npm/foo@1"]);
3547
assert_eq!(args.identifier, "pkg:npm/foo@1");
36-
assert_eq!(args.common.cwd, PathBuf::from("."));
37-
assert_eq!(args.common.manifest_path, ".socket/manifest.json");
3848
assert!(!args.skip_rollback);
39-
assert!(!args.common.yes);
40-
assert!(!args.common.global);
41-
assert_eq!(args.common.global_prefix, None);
42-
assert!(!args.common.json);
49+
assert_eq!(Globals(args.common), opt_out_defaults());
4350
}
4451

4552
#[test]
@@ -49,13 +56,8 @@ fn positional_uuid_stored_in_identifier() {
4956
// Everything else still at default — `remove` does not auto-detect the
5057
// identifier shape at parse time; the runtime branch on `pkg:` happens
5158
// inside `run()`.
52-
assert_eq!(args.common.cwd, PathBuf::from("."));
53-
assert_eq!(args.common.manifest_path, ".socket/manifest.json");
5459
assert!(!args.skip_rollback);
55-
assert!(!args.common.yes);
56-
assert!(!args.common.global);
57-
assert_eq!(args.common.global_prefix, None);
58-
assert!(!args.common.json);
60+
assert_eq!(Globals(args.common), opt_out_defaults());
5961
}
6062

6163
// ---------------------------------------------------------------------------
@@ -153,7 +155,7 @@ fn all_flags_combined() {
153155

154156
#[test]
155157
fn missing_required_positional_is_error() {
156-
let result = Cli::try_parse_from(["socket-patch", "remove"]);
158+
let result = try_parse(&["socket-patch", "remove"]);
157159
let err = match result {
158160
Ok(_) => panic!("remove without identifier must fail"),
159161
Err(e) => e,
@@ -163,7 +165,7 @@ fn missing_required_positional_is_error() {
163165

164166
#[test]
165167
fn unknown_flag_is_error() {
166-
let result = Cli::try_parse_from([
168+
let result = try_parse(&[
167169
"socket-patch",
168170
"remove",
169171
"pkg:npm/foo@1",

0 commit comments

Comments
 (0)