use std::{
os::unix::fs::PermissionsExt as _,
path::Path,
time::{Duration, SystemTime},
};
use serde_json::json;
use super::*;
fn session() -> Value {
json!({
"sessionId": "s1",
"configOptions": [
{"id": "mode", "currentValue": "default", "options": [{"value": "default"}, {"value": "bypassPermissions"}]},
{"id": "model", "currentValue": "default", "options": [
{"value": "default"}, {"value": "opus[1m]"}, {"value": "sonnet"},
{"value": "--dangerous"}, {"value": "@file"}, {"value": "two words"}
]},
{"id": "effort", "currentValue": "default", "options": [
{"value": "default"}, {"value": "low"}, {"value": "xhigh"}, {"value": "x; rm"}, {"value": "-x"}
]}
]
})
}
fn executable(dir: &Path) -> std::path::PathBuf {
let path = dir.join("agent-acp");
std::fs::write(&path, "#!/bin/sh\n").unwrap();
path
}
#[test]
fn a_session_lists_the_model_and_effort_values_that_can_be_passed() {
let options = AgentOptions::from_acp(&session()).unwrap();
let model = options.model.unwrap();
assert_eq!(model.values, ["default", "opus[1m]", "sonnet"]);
assert_eq!(model.shown(), ["opus[1m]", "sonnet"]);
assert_eq!(model.current.as_deref(), Some("default"));
assert_eq!(model.named_default(), None, "`default` names no model");
assert!(model.offers("opus[1m]") && model.offers("default"));
assert!(!model.offers("opus"));
let effort = options.effort.unwrap();
assert_eq!(effort.values, ["default", "low", "xhigh"]);
}
#[test]
fn codex_style_ids_and_named_defaults_are_read() {
let options = AgentOptions::from_acp(&json!({"configOptions": [
{"id": "model", "currentValue": "gpt-5.6-sol", "options": [{"value": "gpt-5.6-sol"}, {"value": "gpt-5.5"}]},
{"id": "reasoning_effort", "currentValue": "xhigh", "options": [{"value": "high"}, {"value": "xhigh"}, {"value": "ultra"}]}
]}))
.unwrap();
assert_eq!(options.model.unwrap().named_default(), Some("gpt-5.6-sol"));
assert_eq!(options.effort.unwrap().shown(), ["high", "xhigh", "ultra"]);
}
#[test]
fn a_session_without_model_or_effort_choices_offers_nothing() {
assert_eq!(AgentOptions::from_acp(&json!({"sessionId": "s1"})), None);
assert_eq!(
AgentOptions::from_acp(&json!({"configOptions": [
{"id": "mode", "options": [{"value": "default"}]},
{"id": "model", "options": [{"value": "--bad"}]}
]})),
None
);
}
#[test]
fn saved_options_load_only_for_the_same_agent_install_and_while_recent() {
let dir = tempfile::tempdir().unwrap();
let server = executable(dir.path());
let file = dir.path().join("state/agent-options/claude.json");
let options = AgentOptions::from_acp(&session()).unwrap();
save(&file, "claude", &server, &options).unwrap();
let mode = |path: &Path| std::fs::metadata(path).unwrap().permissions().mode() & 0o777;
assert_eq!(mode(&file), 0o600);
assert_eq!(mode(file.parent().unwrap()), 0o700);
let now = SystemTime::now();
let listed = load(&file, "claude", &server, now).unwrap();
assert_eq!(listed.options, options);
assert!(listed.fresh(now));
assert!(!listed.fresh(now + MAX_AGE + Duration::from_secs(60)));
assert_eq!(load(&file, "codex", &server, now), None, "another agent");
assert_eq!(
load(
&file,
"claude",
&server,
now + MAX_AGE + Duration::from_secs(60)
),
None,
"too old"
);
let (read, seen) = read_saved(&file, "claude").unwrap();
assert_eq!(read, options);
assert!(seen > 0);
std::fs::write(&server, "#!/bin/sh\n# a newer release\n").unwrap();
assert_eq!(load(&file, "claude", &server, now), None, "another install");
}
#[test]
fn a_saved_file_is_checked_again_when_read() {
let dir = tempfile::tempdir().unwrap();
let server = executable(dir.path());
let file = dir.path().join("state/agent-options/claude.json");
save(&file, "claude", &server, &AgentOptions::default()).unwrap();
let mut saved: Value = serde_json::from_slice(&std::fs::read(&file).unwrap()).unwrap();
saved["model"] = json!({"values": ["sonnet", "--flag", "@x"], "current": "--flag"});
std::fs::write(&file, serde_json::to_vec(&saved).unwrap()).unwrap();
let model = load(&file, "claude", &server, SystemTime::now())
.unwrap()
.options
.model
.unwrap();
assert_eq!(model.values, ["sonnet"]);
assert_eq!(model.current, None);
std::fs::write(&file, vec![b' '; 70 * 1024]).unwrap();
assert_eq!(read_saved(&file, "claude"), None, "oversized");
std::fs::write(&file, "not json").unwrap();
assert_eq!(read_saved(&file, "claude"), None, "malformed");
}