Keep argument parsing independent from plugin discovery
parse_args only needs the tool registry when --allowedTools is present, but it was eagerly normalizing an empty list through the plugin-backed registry. On a clean CI home this made no-arg parsing fail if bundled plugin sync surfaced a broken install, and it also left parse-related tests exposed to concurrent environment mutation. Short-circuit empty allowed-tool normalization and serialize the default-permission parse tests that depend on shared process env. Constraint: Rust CI runs unit tests in parallel inside one process, so std::env mutations are shared across tests Rejected: Keep eager registry loading for empty allowed-tools lists | unnecessary work and leaks unrelated plugin failures into basic arg parsing Confidence: high Scope-risk: narrow Reversibility: clean Directive: Any test that mutates HOME, CLAW_CONFIG_HOME, or RUSTY_CLAUDE_PERMISSION_MODE must hold env_lock while code under test reads process env Tested: cargo fmt --all --check; cargo test -p rusty-claude-cli Not-tested: Additional remote workflows beyond rust-ci
This commit is contained in:
parent
bf59abc9e9
commit
f10bf8f595
1 changed files with 66 additions and 0 deletions
|
|
@ -587,6 +587,9 @@ fn resolve_model_alias(model: &str) -> &str {
|
|||
}
|
||||
|
||||
fn normalize_allowed_tools(values: &[String]) -> Result<Option<AllowedToolSet>, String> {
|
||||
if values.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
current_tool_registry()?.normalize_allowed_tools(values)
|
||||
}
|
||||
|
||||
|
|
@ -5318,6 +5321,7 @@ mod tests {
|
|||
}
|
||||
#[test]
|
||||
fn defaults_to_repl_when_no_args() {
|
||||
let _guard = env_lock();
|
||||
assert_eq!(
|
||||
parse_args(&[]).expect("args should parse"),
|
||||
CliAction::Repl {
|
||||
|
|
@ -5328,6 +5332,62 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn defaults_to_repl_when_no_args_without_loading_plugin_registry() {
|
||||
let _guard = env_lock();
|
||||
let root = temp_dir();
|
||||
let cwd = root.join("workspace");
|
||||
let config_home = root.join("config-home");
|
||||
let bundled_root = cwd.join("broken-bundled");
|
||||
let plugin_root = bundled_root.join("broken-hooks");
|
||||
|
||||
std::fs::create_dir_all(plugin_root.join(".claude-plugin"))
|
||||
.expect("broken bundled manifest dir should exist");
|
||||
std::fs::create_dir_all(&config_home).expect("config home should exist");
|
||||
std::fs::write(
|
||||
plugin_root.join(".claude-plugin").join("plugin.json"),
|
||||
r#"{
|
||||
"name": "broken-hooks",
|
||||
"version": "1.0.0",
|
||||
"description": "broken bundled fixture",
|
||||
"hooks": {
|
||||
"PreToolUse": ["./hooks/pre.sh"]
|
||||
}
|
||||
}"#,
|
||||
)
|
||||
.expect("broken bundled manifest should write");
|
||||
std::fs::write(
|
||||
config_home.join("settings.json"),
|
||||
serde_json::json!({
|
||||
"plugins": {
|
||||
"bundledRoot": "./broken-bundled"
|
||||
}
|
||||
})
|
||||
.to_string(),
|
||||
)
|
||||
.expect("config should write");
|
||||
|
||||
let original_config_home = std::env::var("CLAW_CONFIG_HOME").ok();
|
||||
std::env::set_var("CLAW_CONFIG_HOME", &config_home);
|
||||
|
||||
let action = with_current_dir(&cwd, || parse_args(&[]).expect("args should parse"));
|
||||
|
||||
match original_config_home {
|
||||
Some(value) => std::env::set_var("CLAW_CONFIG_HOME", value),
|
||||
None => std::env::remove_var("CLAW_CONFIG_HOME"),
|
||||
}
|
||||
std::fs::remove_dir_all(root).expect("temp config root should clean up");
|
||||
|
||||
assert_eq!(
|
||||
action,
|
||||
CliAction::Repl {
|
||||
model: DEFAULT_MODEL.to_string(),
|
||||
allowed_tools: None,
|
||||
permission_mode: PermissionMode::DangerFullAccess,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_permission_mode_uses_project_config_when_env_is_unset() {
|
||||
let _guard = env_lock();
|
||||
|
|
@ -5398,6 +5458,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn parses_prompt_subcommand() {
|
||||
let _guard = env_lock();
|
||||
let args = vec![
|
||||
"prompt".to_string(),
|
||||
"hello".to_string(),
|
||||
|
|
@ -5417,6 +5478,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn parses_bare_prompt_and_json_output_flag() {
|
||||
let _guard = env_lock();
|
||||
let args = vec![
|
||||
"--output-format=json".to_string(),
|
||||
"--model".to_string(),
|
||||
|
|
@ -5438,6 +5500,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn resolves_model_aliases_in_args() {
|
||||
let _guard = env_lock();
|
||||
let args = vec![
|
||||
"--model".to_string(),
|
||||
"opus".to_string(),
|
||||
|
|
@ -5491,6 +5554,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn parses_allowed_tools_flags_with_aliases_and_lists() {
|
||||
let _guard = env_lock();
|
||||
let args = vec![
|
||||
"--allowedTools".to_string(),
|
||||
"read,glob".to_string(),
|
||||
|
|
@ -5573,6 +5637,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn parses_single_word_command_aliases_without_falling_back_to_prompt_mode() {
|
||||
let _guard = env_lock();
|
||||
assert_eq!(
|
||||
parse_args(&["help".to_string()]).expect("help should parse"),
|
||||
CliAction::Help
|
||||
|
|
@ -5603,6 +5668,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn multi_word_prompt_still_uses_shorthand_prompt_mode() {
|
||||
let _guard = env_lock();
|
||||
assert_eq!(
|
||||
parse_args(&["help".to_string(), "me".to_string(), "debug".to_string()])
|
||||
.expect("prompt shorthand should still work"),
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue