|
|
@@ -1,6 +1,7 @@
|
|
|
use std::fs;
|
|
|
+use std::path::Path;
|
|
|
use std::path::PathBuf;
|
|
|
-use std::process::Command;
|
|
|
+use std::process::{Command, Output};
|
|
|
use std::sync::atomic::{AtomicU64, Ordering};
|
|
|
use std::time::{SystemTime, UNIX_EPOCH};
|
|
|
|
|
|
@@ -10,6 +11,7 @@ static TEMP_COUNTER: AtomicU64 = AtomicU64::new(0);
|
|
|
|
|
|
#[test]
|
|
|
fn resumed_binary_accepts_slash_commands_with_arguments() {
|
|
|
+ // given
|
|
|
let temp_dir = unique_temp_dir("resume-slash-commands");
|
|
|
fs::create_dir_all(&temp_dir).expect("temp dir should exist");
|
|
|
|
|
|
@@ -24,19 +26,20 @@ fn resumed_binary_accepts_slash_commands_with_arguments() {
|
|
|
.save_to_path(&session_path)
|
|
|
.expect("session should persist");
|
|
|
|
|
|
- let output = Command::new(env!("CARGO_BIN_EXE_claw"))
|
|
|
- .current_dir(&temp_dir)
|
|
|
- .args([
|
|
|
+ // when
|
|
|
+ let output = run_claw(
|
|
|
+ &temp_dir,
|
|
|
+ &[
|
|
|
"--resume",
|
|
|
session_path.to_str().expect("utf8 path"),
|
|
|
"/export",
|
|
|
export_path.to_str().expect("utf8 path"),
|
|
|
"/clear",
|
|
|
"--confirm",
|
|
|
- ])
|
|
|
- .output()
|
|
|
- .expect("claw should launch");
|
|
|
+ ],
|
|
|
+ );
|
|
|
|
|
|
+ // then
|
|
|
assert!(
|
|
|
output.status.success(),
|
|
|
"stdout:\n{}\n\nstderr:\n{}",
|
|
|
@@ -58,6 +61,161 @@ fn resumed_binary_accepts_slash_commands_with_arguments() {
|
|
|
assert!(restored.messages.is_empty());
|
|
|
}
|
|
|
|
|
|
+#[test]
|
|
|
+fn status_command_applies_cli_flags_end_to_end() {
|
|
|
+ // given
|
|
|
+ let temp_dir = unique_temp_dir("status-command-flags");
|
|
|
+ fs::create_dir_all(&temp_dir).expect("temp dir should exist");
|
|
|
+
|
|
|
+ // when
|
|
|
+ let output = run_claw(
|
|
|
+ &temp_dir,
|
|
|
+ &[
|
|
|
+ "--model",
|
|
|
+ "sonnet",
|
|
|
+ "--permission-mode",
|
|
|
+ "read-only",
|
|
|
+ "status",
|
|
|
+ ],
|
|
|
+ );
|
|
|
+
|
|
|
+ // then
|
|
|
+ assert!(
|
|
|
+ output.status.success(),
|
|
|
+ "stdout:\n{}\n\nstderr:\n{}",
|
|
|
+ String::from_utf8_lossy(&output.stdout),
|
|
|
+ String::from_utf8_lossy(&output.stderr)
|
|
|
+ );
|
|
|
+
|
|
|
+ let stdout = String::from_utf8(output.stdout).expect("stdout should be utf8");
|
|
|
+ assert!(stdout.contains("Status"));
|
|
|
+ assert!(stdout.contains("Model claude-sonnet-4-6"));
|
|
|
+ assert!(stdout.contains("Permission mode read-only"));
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn resumed_config_command_loads_settings_files_end_to_end() {
|
|
|
+ // given
|
|
|
+ let temp_dir = unique_temp_dir("resume-config");
|
|
|
+ let project_dir = temp_dir.join("project");
|
|
|
+ let config_home = temp_dir.join("home").join(".claw");
|
|
|
+ fs::create_dir_all(project_dir.join(".claw")).expect("project config dir should exist");
|
|
|
+ fs::create_dir_all(&config_home).expect("config home should exist");
|
|
|
+
|
|
|
+ let session_path = project_dir.join("session.jsonl");
|
|
|
+ Session::new()
|
|
|
+ .with_persistence_path(&session_path)
|
|
|
+ .save_to_path(&session_path)
|
|
|
+ .expect("session should persist");
|
|
|
+
|
|
|
+ fs::write(config_home.join("settings.json"), r#"{"model":"haiku"}"#)
|
|
|
+ .expect("user config should write");
|
|
|
+ fs::write(
|
|
|
+ project_dir.join(".claw").join("settings.local.json"),
|
|
|
+ r#"{"model":"opus"}"#,
|
|
|
+ )
|
|
|
+ .expect("local config should write");
|
|
|
+
|
|
|
+ // when
|
|
|
+ let output = run_claw_with_env(
|
|
|
+ &project_dir,
|
|
|
+ &[
|
|
|
+ "--resume",
|
|
|
+ session_path.to_str().expect("utf8 path"),
|
|
|
+ "/config",
|
|
|
+ "model",
|
|
|
+ ],
|
|
|
+ &[("CLAW_CONFIG_HOME", config_home.to_str().expect("utf8 path"))],
|
|
|
+ );
|
|
|
+
|
|
|
+ // then
|
|
|
+ assert!(
|
|
|
+ output.status.success(),
|
|
|
+ "stdout:\n{}\n\nstderr:\n{}",
|
|
|
+ String::from_utf8_lossy(&output.stdout),
|
|
|
+ String::from_utf8_lossy(&output.stderr)
|
|
|
+ );
|
|
|
+
|
|
|
+ let stdout = String::from_utf8(output.stdout).expect("stdout should be utf8");
|
|
|
+ assert!(stdout.contains("Config"));
|
|
|
+ assert!(stdout.contains("Loaded files 2"));
|
|
|
+ assert!(stdout.contains(
|
|
|
+ config_home
|
|
|
+ .join("settings.json")
|
|
|
+ .to_str()
|
|
|
+ .expect("utf8 path")
|
|
|
+ ));
|
|
|
+ assert!(stdout.contains(
|
|
|
+ project_dir
|
|
|
+ .join(".claw")
|
|
|
+ .join("settings.local.json")
|
|
|
+ .to_str()
|
|
|
+ .expect("utf8 path")
|
|
|
+ ));
|
|
|
+ assert!(stdout.contains("Merged section: model"));
|
|
|
+ assert!(stdout.contains("opus"));
|
|
|
+}
|
|
|
+
|
|
|
+#[test]
|
|
|
+fn resume_latest_restores_the_most_recent_managed_session() {
|
|
|
+ // given
|
|
|
+ let temp_dir = unique_temp_dir("resume-latest");
|
|
|
+ let project_dir = temp_dir.join("project");
|
|
|
+ let sessions_dir = project_dir.join(".claw").join("sessions");
|
|
|
+ fs::create_dir_all(&sessions_dir).expect("sessions dir should exist");
|
|
|
+
|
|
|
+ let older_path = sessions_dir.join("session-older.jsonl");
|
|
|
+ let newer_path = sessions_dir.join("session-newer.jsonl");
|
|
|
+
|
|
|
+ let mut older = Session::new().with_persistence_path(&older_path);
|
|
|
+ older
|
|
|
+ .push_user_text("older session")
|
|
|
+ .expect("older session write should succeed");
|
|
|
+ older
|
|
|
+ .save_to_path(&older_path)
|
|
|
+ .expect("older session should persist");
|
|
|
+
|
|
|
+ let mut newer = Session::new().with_persistence_path(&newer_path);
|
|
|
+ newer
|
|
|
+ .push_user_text("newer session")
|
|
|
+ .expect("newer session write should succeed");
|
|
|
+ newer
|
|
|
+ .push_user_text("resume me")
|
|
|
+ .expect("newer session write should succeed");
|
|
|
+ newer
|
|
|
+ .save_to_path(&newer_path)
|
|
|
+ .expect("newer session should persist");
|
|
|
+
|
|
|
+ // when
|
|
|
+ let output = run_claw(&project_dir, &["--resume", "latest", "/status"]);
|
|
|
+
|
|
|
+ // then
|
|
|
+ assert!(
|
|
|
+ output.status.success(),
|
|
|
+ "stdout:\n{}\n\nstderr:\n{}",
|
|
|
+ String::from_utf8_lossy(&output.stdout),
|
|
|
+ String::from_utf8_lossy(&output.stderr)
|
|
|
+ );
|
|
|
+
|
|
|
+ let stdout = String::from_utf8(output.stdout).expect("stdout should be utf8");
|
|
|
+ assert!(stdout.contains("Status"));
|
|
|
+ assert!(stdout.contains("Messages 2"));
|
|
|
+ assert!(stdout.contains(newer_path.to_str().expect("utf8 path")));
|
|
|
+}
|
|
|
+
|
|
|
+fn run_claw(current_dir: &Path, args: &[&str]) -> Output {
|
|
|
+ run_claw_with_env(current_dir, args, &[])
|
|
|
+}
|
|
|
+
|
|
|
+fn run_claw_with_env(current_dir: &Path, args: &[&str], envs: &[(&str, &str)]) -> Output {
|
|
|
+ let mut command = Command::new(env!("CARGO_BIN_EXE_claw"));
|
|
|
+ command.current_dir(current_dir).args(args);
|
|
|
+ for (key, value) in envs {
|
|
|
+ command.env(key, value);
|
|
|
+ }
|
|
|
+ command.output().expect("claw should launch")
|
|
|
+}
|
|
|
+
|
|
|
fn unique_temp_dir(label: &str) -> PathBuf {
|
|
|
let millis = SystemTime::now()
|
|
|
.duration_since(UNIX_EPOCH)
|