use super::*;
#[test]
fn a_default_claude_launch_keeps_completed_thinking_visible() {
for models in [
json!([
{"id": "future-native-id", "owned_by": "anthropic"},
{"id": "sonnet", "owned_by": "z.ai", "client_capabilities": {"claude": {"behaves_as": "claude-sonnet-4-5", "source": "provider-protocol:z.ai-anthropic"}}}
]),
json!([]),
] {
let profiles = tempfile::tempdir().expect("profile root");
let models: Vec<RouterModel> =
serde_json::from_value(models).expect("deserialize catalog fixture");
let prepared = TemporaryClient::prepare(&Preparation {
client: ClientKind::ClaudeCode,
base_url: "http://router.test",
token: "task-token",
model_override: None,
models: &models,
isolated_config: false,
extend_user_configuration: false,
one_shot: false,
user_model_selection: None,
profile_root: Some(profiles.path()),
codex_reasoning_effort: None,
codex_backend_base_url: None,
ca_cert: None,
})
.expect("prepare a default Claude launch");
let arguments = prepared
.command
.get_args()
.map(|argument| argument.to_string_lossy().into_owned())
.collect::<Vec<_>>();
let settings = arguments
.windows(2)
.find_map(|pair| (pair[0] == "--settings").then_some(&pair[1]))
.expect("a bare launch must still carry process-local settings");
let settings: serde_json::Value =
serde_json::from_str(settings).expect("valid settings JSON");
assert_eq!(
settings.get("verbose"),
Some(&json!(true)),
"a bare launch must keep completed thinking visible: {settings}"
);
assert_eq!(
settings.get("modelPicker"),
None,
"no filtered rows means no picker to write: {settings}"
);
assert_eq!(
settings.as_object().map(serde_json::Map::len),
Some(1),
"the process-local settings carry nothing else: {settings}"
);
}
}
#[test]
fn router_settings_precede_forwarded_claude_arguments() {
let profiles = tempfile::tempdir().expect("profile root");
let models: Vec<RouterModel> = serde_json::from_value(json!([
{"id": "future-glm-alpha", "owned_by": "z.ai", "client_capabilities": {"claude": {"behaves_as": "claude-sonnet-4-5", "source": "provider-protocol:z.ai-anthropic"}}}
]))
.expect("deserialize catalog fixture");
let mut prepared = TemporaryClient::prepare(&Preparation {
client: ClientKind::ClaudeCode,
base_url: "http://router.test",
token: "task-token",
model_override: None,
models: &models,
isolated_config: false,
extend_user_configuration: false,
one_shot: false,
user_model_selection: None,
profile_root: Some(profiles.path()),
codex_reasoning_effort: None,
codex_backend_base_url: None,
ca_cert: None,
})
.expect("prepare a default Claude launch");
prepared
.command
.args([std::ffi::OsString::from("--settings"), "{}".into()]);
let arguments = prepared
.command
.get_args()
.map(|argument| argument.to_string_lossy().into_owned())
.collect::<Vec<_>>();
let occurrences = arguments
.iter()
.enumerate()
.filter_map(|(index, argument)| (argument == "--settings").then_some(index))
.collect::<Vec<_>>();
assert_eq!(occurrences.len(), 2, "{arguments:?}");
assert!(
occurrences[0] < occurrences[1],
"Router's settings must come first so the user's override wins: {arguments:?}"
);
let router_settings: serde_json::Value =
serde_json::from_str(&arguments[occurrences[0] + 1]).expect("valid settings JSON");
assert_eq!(router_settings.get("verbose"), Some(&json!(true)));
}
#[test]
fn a_saved_model_choice_is_never_overridden_by_a_router_pin() {
let zai = json!([
{"id": "glm-4.5", "owned_by": "z.ai", "provider_created_at": 1, "client_capabilities": {"claude": {"behaves_as": "claude-sonnet-4-5", "source": "provider-protocol:z.ai-anthropic"}}},
{"id": "glm-5.3-flash", "owned_by": "z.ai", "provider_created_at": 9, "client_capabilities": {"claude": {"behaves_as": "claude-sonnet-4-5", "source": "provider-protocol:z.ai-anthropic"}}}
]);
let models: Vec<RouterModel> =
serde_json::from_value(zai).expect("deserialize profiled z.ai models");
let pins = |profiles: &std::path::Path, selection: Option<&str>| {
let prepared = TemporaryClient::prepare(&Preparation {
client: ClientKind::ClaudeCode,
base_url: "http://router.test",
token: "task-token",
model_override: None,
models: &models,
isolated_config: false,
extend_user_configuration: false,
one_shot: false,
user_model_selection: selection,
profile_root: Some(profiles),
codex_reasoning_effort: None,
codex_backend_base_url: None,
ca_cert: None,
})
.expect("prepare a z.ai-only Claude session");
prepared
.command
.get_envs()
.filter_map(|(key, value)| {
Some((
key.to_string_lossy().into_owned(),
value?.to_string_lossy().into_owned(),
))
})
.collect::<std::collections::HashMap<_, _>>()
};
let fresh = tempfile::tempdir().expect("profile root");
let supplied = pins(fresh.path(), None);
for key in crate::clients::CLAUDE_GATEWAY_TARGET_ENV {
assert_eq!(
supplied.get(key).map(String::as_str),
Some("glm-5.3-flash"),
"{key} must fall back to the current model"
);
}
let with_env = tempfile::tempdir().expect("profile root");
let respected = pins(with_env.path(), Some("glm-5.3"));
for key in crate::clients::CLAUDE_GATEWAY_TARGET_ENV {
assert!(
!respected.contains_key(key),
"{key} must not be overwritten when the user chose a model"
);
}
let saved = tempfile::tempdir().expect("profile root");
let profile = saved
.path()
.join("link-assistant-router/clients/claude/home/.claude");
std::fs::create_dir_all(&profile).expect("create the Router-owned Claude profile");
std::fs::write(
profile.join("settings.json"),
br#"{"model":"glm-5.3-flash","verbose":true}"#,
)
.expect("seed a saved model choice");
let honoured = pins(saved.path(), None);
for key in crate::clients::CLAUDE_GATEWAY_TARGET_ENV {
assert!(
!honoured.contains_key(key),
"{key} must not override the model saved in the profile"
);
}
let unsaved = tempfile::tempdir().expect("profile root");
let other = unsaved
.path()
.join("link-assistant-router/clients/claude/home/.claude");
std::fs::create_dir_all(&other).expect("create the Router-owned Claude profile");
std::fs::write(other.join("settings.json"), br#"{"verbose":true}"#)
.expect("seed a profile with no model");
let still_supplied = pins(unsaved.path(), None);
for key in crate::clients::CLAUDE_GATEWAY_TARGET_ENV {
assert_eq!(
still_supplied.get(key).map(String::as_str),
Some("glm-5.3-flash"),
"{key} must still fall back when nothing was chosen"
);
}
}