diff --git a/src/auth/profiles.rs b/src/auth/profiles.rs index 48ba6ce..39d39ee 100644 --- a/src/auth/profiles.rs +++ b/src/auth/profiles.rs @@ -626,8 +626,8 @@ mod tests { assert!(!token_set.is_expiring_within(Duration::from_secs(1))); } - #[test] - fn store_roundtrip_with_encryption() { + #[tokio::test] + async fn store_roundtrip_with_encryption() { let tmp = TempDir::new().unwrap(); let store = AuthProfilesStore::new(tmp.path(), true); @@ -661,14 +661,14 @@ mod tests { Some("refresh-123") ); - let raw = fs::read_to_string(store.path()).unwrap(); + let raw = tokio::fs::read_to_string(store.path()).await.unwrap(); assert!(raw.contains("enc2:")); assert!(!raw.contains("refresh-123")); assert!(!raw.contains("access-123")); } - #[test] - fn atomic_write_replaces_file() { + #[tokio::test] + async fn atomic_write_replaces_file() { let tmp = TempDir::new().unwrap(); let store = AuthProfilesStore::new(tmp.path(), false); @@ -678,7 +678,7 @@ mod tests { let path = store.path().to_path_buf(); assert!(path.exists()); - let contents = fs::read_to_string(path).unwrap(); + let contents = tokio::fs::read_to_string(path).await.unwrap(); assert!(contents.contains("\"schema_version\": 1")); } } diff --git a/src/config/schema.rs b/src/config/schema.rs index c1293ac..a4127d4 100644 --- a/src/config/schema.rs +++ b/src/config/schema.rs @@ -3385,8 +3385,8 @@ tool_dispatcher = "xml" assert_eq!(parsed.agent.tool_dispatcher, "xml"); } - #[test] - fn config_save_and_load_tmpdir() { + #[tokio::test] + async fn config_save_and_load_tmpdir() { let dir = std::env::temp_dir().join("zeroclaw_test_config"); let _ = fs::remove_dir_all(&dir); fs::create_dir_all(&dir).unwrap(); @@ -3431,7 +3431,7 @@ tool_dispatcher = "xml" config.save().unwrap(); assert!(config_path.exists()); - let contents = fs::read_to_string(&config_path).unwrap(); + let contents = tokio::fs::read_to_string(&config_path).await.unwrap(); let loaded: Config = toml::from_str(&contents).unwrap(); assert!(loaded .api_key @@ -3446,8 +3446,8 @@ tool_dispatcher = "xml" let _ = fs::remove_dir_all(&dir); } - #[test] - fn config_save_encrypts_nested_credentials() { + #[tokio::test] + async fn config_save_encrypts_nested_credentials() { let dir = std::env::temp_dir().join(format!( "zeroclaw_test_nested_credentials_{}", uuid::Uuid::new_v4() @@ -3477,7 +3477,9 @@ tool_dispatcher = "xml" config.save().unwrap(); - let contents = fs::read_to_string(config.config_path.clone()).unwrap(); + let contents = tokio::fs::read_to_string(config.config_path.clone()) + .await + .unwrap(); let stored: Config = toml::from_str(&contents).unwrap(); let store = crate::security::SecretStore::new(&dir, true); @@ -3527,8 +3529,8 @@ tool_dispatcher = "xml" let _ = fs::remove_dir_all(&dir); } - #[test] - fn config_save_atomic_cleanup() { + #[tokio::test] + async fn config_save_atomic_cleanup() { let dir = std::env::temp_dir().join(format!("zeroclaw_test_config_{}", uuid::Uuid::new_v4())); fs::create_dir_all(&dir).unwrap(); @@ -3545,7 +3547,7 @@ tool_dispatcher = "xml" config.default_model = Some("model-b".into()); config.save().unwrap(); - let contents = fs::read_to_string(&config_path).unwrap(); + let contents = tokio::fs::read_to_string(&config_path).await.unwrap(); assert!(contents.contains("model-b")); let names: Vec = fs::read_dir(&dir) diff --git a/src/cron/scheduler.rs b/src/cron/scheduler.rs index ce9c6c3..45bb38b 100644 --- a/src/cron/scheduler.rs +++ b/src/cron/scheduler.rs @@ -475,13 +475,13 @@ mod tests { use chrono::{Duration as ChronoDuration, Utc}; use tempfile::TempDir; - fn test_config(tmp: &TempDir) -> Config { + async fn test_config(tmp: &TempDir) -> Config { let config = Config { workspace_dir: tmp.path().join("workspace"), config_path: tmp.path().join("config.toml"), ..Config::default() }; - std::fs::create_dir_all(&config.workspace_dir).unwrap(); + tokio::fs::create_dir_all(&config.workspace_dir).await.unwrap(); config } @@ -513,7 +513,7 @@ mod tests { #[tokio::test] async fn run_job_command_success() { let tmp = TempDir::new().unwrap(); - let config = test_config(&tmp); + let config = test_config(&tmp).await; let job = test_job("echo scheduler-ok"); let security = SecurityPolicy::from_config(&config.autonomy, &config.workspace_dir); @@ -526,7 +526,7 @@ mod tests { #[tokio::test] async fn run_job_command_failure() { let tmp = TempDir::new().unwrap(); - let config = test_config(&tmp); + let config = test_config(&tmp).await; let job = test_job("ls definitely_missing_file_for_scheduler_test"); let security = SecurityPolicy::from_config(&config.autonomy, &config.workspace_dir); @@ -539,7 +539,7 @@ mod tests { #[tokio::test] async fn run_job_command_times_out() { let tmp = TempDir::new().unwrap(); - let mut config = test_config(&tmp); + let mut config = test_config(&tmp).await; config.autonomy.allowed_commands = vec!["sleep".into()]; let job = test_job("sleep 1"); let security = SecurityPolicy::from_config(&config.autonomy, &config.workspace_dir); @@ -553,7 +553,7 @@ mod tests { #[tokio::test] async fn run_job_command_blocks_disallowed_command() { let tmp = TempDir::new().unwrap(); - let mut config = test_config(&tmp); + let mut config = test_config(&tmp).await; config.autonomy.allowed_commands = vec!["echo".into()]; let job = test_job("curl https://evil.example"); let security = SecurityPolicy::from_config(&config.autonomy, &config.workspace_dir); @@ -567,7 +567,7 @@ mod tests { #[tokio::test] async fn run_job_command_blocks_forbidden_path_argument() { let tmp = TempDir::new().unwrap(); - let mut config = test_config(&tmp); + let mut config = test_config(&tmp).await; config.autonomy.allowed_commands = vec!["cat".into()]; let job = test_job("cat /etc/passwd"); let security = SecurityPolicy::from_config(&config.autonomy, &config.workspace_dir); @@ -582,7 +582,7 @@ mod tests { #[tokio::test] async fn run_job_command_blocks_readonly_mode() { let tmp = TempDir::new().unwrap(); - let mut config = test_config(&tmp); + let mut config = test_config(&tmp).await; config.autonomy.level = crate::security::AutonomyLevel::ReadOnly; let job = test_job("echo should-not-run"); let security = SecurityPolicy::from_config(&config.autonomy, &config.workspace_dir); @@ -596,7 +596,7 @@ mod tests { #[tokio::test] async fn run_job_command_blocks_rate_limited() { let tmp = TempDir::new().unwrap(); - let mut config = test_config(&tmp); + let mut config = test_config(&tmp).await; config.autonomy.max_actions_per_hour = 0; let job = test_job("echo should-not-run"); let security = SecurityPolicy::from_config(&config.autonomy, &config.workspace_dir); @@ -610,16 +610,17 @@ mod tests { #[tokio::test] async fn execute_job_with_retry_recovers_after_first_failure() { let tmp = TempDir::new().unwrap(); - let mut config = test_config(&tmp); + let mut config = test_config(&tmp).await; config.reliability.scheduler_retries = 1; config.reliability.provider_backoff_ms = 1; config.autonomy.allowed_commands = vec!["sh".into()]; let security = SecurityPolicy::from_config(&config.autonomy, &config.workspace_dir); - std::fs::write( + tokio::fs::write( config.workspace_dir.join("retry-once.sh"), "#!/bin/sh\nif [ -f retry-ok.flag ]; then\n echo recovered\n exit 0\nfi\ntouch retry-ok.flag\nexit 1\n", ) + .await .unwrap(); let job = test_job("sh ./retry-once.sh"); @@ -631,7 +632,7 @@ mod tests { #[tokio::test] async fn execute_job_with_retry_exhausts_attempts() { let tmp = TempDir::new().unwrap(); - let mut config = test_config(&tmp); + let mut config = test_config(&tmp).await; config.reliability.scheduler_retries = 1; config.reliability.provider_backoff_ms = 1; let security = SecurityPolicy::from_config(&config.autonomy, &config.workspace_dir); @@ -646,7 +647,7 @@ mod tests { #[tokio::test] async fn run_agent_job_returns_error_without_provider_key() { let tmp = TempDir::new().unwrap(); - let config = test_config(&tmp); + let config = test_config(&tmp).await; let mut job = test_job(""); job.job_type = JobType::Agent; job.prompt = Some("Say hello".into()); @@ -662,7 +663,7 @@ mod tests { #[tokio::test] async fn persist_job_result_records_run_and_reschedules_shell_job() { let tmp = TempDir::new().unwrap(); - let config = test_config(&tmp); + let config = test_config(&tmp).await; let job = cron::add_job(&config, "*/5 * * * *", "echo ok").unwrap(); let started = Utc::now(); let finished = started + ChronoDuration::milliseconds(10); @@ -679,7 +680,7 @@ mod tests { #[tokio::test] async fn persist_job_result_success_deletes_one_shot() { let tmp = TempDir::new().unwrap(); - let config = test_config(&tmp); + let config = test_config(&tmp).await; let at = Utc::now() + ChronoDuration::minutes(10); let job = cron::add_agent_job( &config, @@ -704,7 +705,7 @@ mod tests { #[tokio::test] async fn persist_job_result_failure_disables_one_shot() { let tmp = TempDir::new().unwrap(); - let config = test_config(&tmp); + let config = test_config(&tmp).await; let at = Utc::now() + ChronoDuration::minutes(10); let job = cron::add_agent_job( &config, @@ -730,7 +731,7 @@ mod tests { #[tokio::test] async fn deliver_if_configured_handles_none_and_invalid_channel() { let tmp = TempDir::new().unwrap(); - let config = test_config(&tmp); + let config = test_config(&tmp).await; let mut job = test_job("echo ok"); assert!(deliver_if_configured(&config, &job, "x").await.is_ok()); diff --git a/src/gateway/mod.rs b/src/gateway/mod.rs index 4f8246c..8637eb6 100644 --- a/src/gateway/mod.rs +++ b/src/gateway/mod.rs @@ -1381,8 +1381,8 @@ mod tests { assert_eq!(normalize_max_keys(1, 10_000), 1); } - #[test] - fn persist_pairing_tokens_writes_config_tokens() { + #[tokio::test] + async fn persist_pairing_tokens_writes_config_tokens() { let temp = tempfile::tempdir().unwrap(); let config_path = temp.path().join("config.toml"); let workspace_path = temp.path().join("workspace"); @@ -1400,7 +1400,7 @@ mod tests { let shared_config = Arc::new(Mutex::new(config)); persist_pairing_tokens(&shared_config, &guard).unwrap(); - let saved = std::fs::read_to_string(config_path).unwrap(); + let saved = tokio::fs::read_to_string(config_path).await.unwrap(); let parsed: Config = toml::from_str(&saved).unwrap(); assert_eq!(parsed.gateway.paired_tokens.len(), 1); let persisted = &parsed.gateway.paired_tokens[0]; diff --git a/src/memory/lucid.rs b/src/memory/lucid.rs index 62af08f..763f5f7 100644 --- a/src/memory/lucid.rs +++ b/src/memory/lucid.rs @@ -608,7 +608,7 @@ exit 1 .iter() .any(|e| e.content.contains("Rust should stay local-first"))); - let context_calls = fs::read_to_string(&marker).unwrap_or_default(); + let context_calls = tokio::fs::read_to_string(&marker).await.unwrap_or_default(); assert!( context_calls.trim().is_empty(), "Expected local-hit short-circuit; got calls: {context_calls}" @@ -669,7 +669,7 @@ exit 1 assert!(first.is_empty()); assert!(second.is_empty()); - let calls = fs::read_to_string(&marker).unwrap_or_default(); + let calls = tokio::fs::read_to_string(&marker).await.unwrap_or_default(); assert_eq!(calls.lines().count(), 1); } } diff --git a/src/memory/markdown.rs b/src/memory/markdown.rs index 9038683..5bc093f 100644 --- a/src/memory/markdown.rs +++ b/src/memory/markdown.rs @@ -229,7 +229,6 @@ impl Memory for MarkdownMemory { #[cfg(test)] mod tests { use super::*; - use std::fs as sync_fs; use tempfile::TempDir; fn temp_workspace() -> (TempDir, MarkdownMemory) { @@ -256,7 +255,7 @@ mod tests { mem.store("pref", "User likes Rust", MemoryCategory::Core, None) .await .unwrap(); - let content = sync_fs::read_to_string(mem.core_path()).unwrap(); + let content = fs::read_to_string(mem.core_path()).await.unwrap(); assert!(content.contains("User likes Rust")); } @@ -267,7 +266,7 @@ mod tests { .await .unwrap(); let path = mem.daily_path(); - let content = sync_fs::read_to_string(path).unwrap(); + let content = fs::read_to_string(path).await.unwrap(); assert!(content.contains("Finished tests")); } diff --git a/src/onboard/wizard.rs b/src/onboard/wizard.rs index 31df793..c3c9db0 100644 --- a/src/onboard/wizard.rs +++ b/src/onboard/wizard.rs @@ -4537,8 +4537,8 @@ mod tests { // ── scaffold_workspace: personalization ───────────────────── - #[test] - fn scaffold_bakes_user_name_into_files() { + #[tokio::test] + async fn scaffold_bakes_user_name_into_files() { let tmp = TempDir::new().unwrap(); let ctx = ProjectContext { user_name: "Alice".into(), @@ -4546,21 +4546,25 @@ mod tests { }; scaffold_workspace(tmp.path(), &ctx).unwrap(); - let user_md = fs::read_to_string(tmp.path().join("USER.md")).unwrap(); + let user_md = tokio::fs::read_to_string(tmp.path().join("USER.md")) + .await + .unwrap(); assert!( user_md.contains("**Name:** Alice"), "USER.md should contain user name" ); - let bootstrap = fs::read_to_string(tmp.path().join("BOOTSTRAP.md")).unwrap(); + let bootstrap = tokio::fs::read_to_string(tmp.path().join("BOOTSTRAP.md")) + .await + .unwrap(); assert!( bootstrap.contains("**Alice**"), "BOOTSTRAP.md should contain user name" ); } - #[test] - fn scaffold_bakes_timezone_into_files() { + #[tokio::test] + async fn scaffold_bakes_timezone_into_files() { let tmp = TempDir::new().unwrap(); let ctx = ProjectContext { timezone: "US/Pacific".into(), @@ -4568,21 +4572,25 @@ mod tests { }; scaffold_workspace(tmp.path(), &ctx).unwrap(); - let user_md = fs::read_to_string(tmp.path().join("USER.md")).unwrap(); + let user_md = tokio::fs::read_to_string(tmp.path().join("USER.md")) + .await + .unwrap(); assert!( user_md.contains("**Timezone:** US/Pacific"), "USER.md should contain timezone" ); - let bootstrap = fs::read_to_string(tmp.path().join("BOOTSTRAP.md")).unwrap(); + let bootstrap = tokio::fs::read_to_string(tmp.path().join("BOOTSTRAP.md")) + .await + .unwrap(); assert!( bootstrap.contains("US/Pacific"), "BOOTSTRAP.md should contain timezone" ); } - #[test] - fn scaffold_bakes_agent_name_into_files() { + #[tokio::test] + async fn scaffold_bakes_agent_name_into_files() { let tmp = TempDir::new().unwrap(); let ctx = ProjectContext { agent_name: "Crabby".into(), @@ -4590,39 +4598,49 @@ mod tests { }; scaffold_workspace(tmp.path(), &ctx).unwrap(); - let identity = fs::read_to_string(tmp.path().join("IDENTITY.md")).unwrap(); + let identity = tokio::fs::read_to_string(tmp.path().join("IDENTITY.md")) + .await + .unwrap(); assert!( identity.contains("**Name:** Crabby"), "IDENTITY.md should contain agent name" ); - let soul = fs::read_to_string(tmp.path().join("SOUL.md")).unwrap(); + let soul = tokio::fs::read_to_string(tmp.path().join("SOUL.md")) + .await + .unwrap(); assert!( soul.contains("You are **Crabby**"), "SOUL.md should contain agent name" ); - let agents = fs::read_to_string(tmp.path().join("AGENTS.md")).unwrap(); + let agents = tokio::fs::read_to_string(tmp.path().join("AGENTS.md")) + .await + .unwrap(); assert!( agents.contains("Crabby Personal Assistant"), "AGENTS.md should contain agent name" ); - let heartbeat = fs::read_to_string(tmp.path().join("HEARTBEAT.md")).unwrap(); + let heartbeat = tokio::fs::read_to_string(tmp.path().join("HEARTBEAT.md")) + .await + .unwrap(); assert!( heartbeat.contains("Crabby"), "HEARTBEAT.md should contain agent name" ); - let bootstrap = fs::read_to_string(tmp.path().join("BOOTSTRAP.md")).unwrap(); + let bootstrap = tokio::fs::read_to_string(tmp.path().join("BOOTSTRAP.md")) + .await + .unwrap(); assert!( bootstrap.contains("Introduce yourself as Crabby"), "BOOTSTRAP.md should contain agent name" ); } - #[test] - fn scaffold_bakes_communication_style() { + #[tokio::test] + async fn scaffold_bakes_communication_style() { let tmp = TempDir::new().unwrap(); let ctx = ProjectContext { communication_style: "Be technical and detailed.".into(), @@ -4630,19 +4648,25 @@ mod tests { }; scaffold_workspace(tmp.path(), &ctx).unwrap(); - let soul = fs::read_to_string(tmp.path().join("SOUL.md")).unwrap(); + let soul = tokio::fs::read_to_string(tmp.path().join("SOUL.md")) + .await + .unwrap(); assert!( soul.contains("Be technical and detailed."), "SOUL.md should contain communication style" ); - let user_md = fs::read_to_string(tmp.path().join("USER.md")).unwrap(); + let user_md = tokio::fs::read_to_string(tmp.path().join("USER.md")) + .await + .unwrap(); assert!( user_md.contains("Be technical and detailed."), "USER.md should contain communication style" ); - let bootstrap = fs::read_to_string(tmp.path().join("BOOTSTRAP.md")).unwrap(); + let bootstrap = tokio::fs::read_to_string(tmp.path().join("BOOTSTRAP.md")) + .await + .unwrap(); assert!( bootstrap.contains("Be technical and detailed."), "BOOTSTRAP.md should contain communication style" @@ -4651,19 +4675,23 @@ mod tests { // ── scaffold_workspace: defaults when context is empty ────── - #[test] - fn scaffold_uses_defaults_for_empty_context() { + #[tokio::test] + async fn scaffold_uses_defaults_for_empty_context() { let tmp = TempDir::new().unwrap(); let ctx = ProjectContext::default(); // all empty scaffold_workspace(tmp.path(), &ctx).unwrap(); - let identity = fs::read_to_string(tmp.path().join("IDENTITY.md")).unwrap(); + let identity = tokio::fs::read_to_string(tmp.path().join("IDENTITY.md")) + .await + .unwrap(); assert!( identity.contains("**Name:** ZeroClaw"), "should default agent name to ZeroClaw" ); - let user_md = fs::read_to_string(tmp.path().join("USER.md")).unwrap(); + let user_md = tokio::fs::read_to_string(tmp.path().join("USER.md")) + .await + .unwrap(); assert!( user_md.contains("**Name:** User"), "should default user name to User" @@ -4673,7 +4701,9 @@ mod tests { "should default timezone to UTC" ); - let soul = fs::read_to_string(tmp.path().join("SOUL.md")).unwrap(); + let soul = tokio::fs::read_to_string(tmp.path().join("SOUL.md")) + .await + .unwrap(); assert!( soul.contains("Be warm, natural, and clear."), "should default communication style" @@ -4682,8 +4712,8 @@ mod tests { // ── scaffold_workspace: skip existing files ───────────────── - #[test] - fn scaffold_does_not_overwrite_existing_files() { + #[tokio::test] + async fn scaffold_does_not_overwrite_existing_files() { let tmp = TempDir::new().unwrap(); let ctx = ProjectContext { user_name: "Bob".into(), @@ -4697,7 +4727,7 @@ mod tests { scaffold_workspace(tmp.path(), &ctx).unwrap(); // SOUL.md should be untouched - let soul = fs::read_to_string(&soul_path).unwrap(); + let soul = tokio::fs::read_to_string(&soul_path).await.unwrap(); assert!( soul.contains("Do not overwrite me"), "existing files should not be overwritten" @@ -4708,14 +4738,16 @@ mod tests { ); // But USER.md should be created fresh - let user_md = fs::read_to_string(tmp.path().join("USER.md")).unwrap(); + let user_md = tokio::fs::read_to_string(tmp.path().join("USER.md")) + .await + .unwrap(); assert!(user_md.contains("**Name:** Bob")); } // ── scaffold_workspace: idempotent ────────────────────────── - #[test] - fn scaffold_is_idempotent() { + #[tokio::test] + async fn scaffold_is_idempotent() { let tmp = TempDir::new().unwrap(); let ctx = ProjectContext { user_name: "Eve".into(), @@ -4724,19 +4756,23 @@ mod tests { }; scaffold_workspace(tmp.path(), &ctx).unwrap(); - let soul_v1 = fs::read_to_string(tmp.path().join("SOUL.md")).unwrap(); + let soul_v1 = tokio::fs::read_to_string(tmp.path().join("SOUL.md")) + .await + .unwrap(); // Run again — should not change anything scaffold_workspace(tmp.path(), &ctx).unwrap(); - let soul_v2 = fs::read_to_string(tmp.path().join("SOUL.md")).unwrap(); + let soul_v2 = tokio::fs::read_to_string(tmp.path().join("SOUL.md")) + .await + .unwrap(); assert_eq!(soul_v1, soul_v2, "scaffold should be idempotent"); } // ── scaffold_workspace: all files are non-empty ───────────── - #[test] - fn scaffold_files_are_non_empty() { + #[tokio::test] + async fn scaffold_files_are_non_empty() { let tmp = TempDir::new().unwrap(); let ctx = ProjectContext::default(); scaffold_workspace(tmp.path(), &ctx).unwrap(); @@ -4751,20 +4787,22 @@ mod tests { "BOOTSTRAP.md", "MEMORY.md", ] { - let content = fs::read_to_string(tmp.path().join(f)).unwrap(); + let content = tokio::fs::read_to_string(tmp.path().join(f)).await.unwrap(); assert!(!content.trim().is_empty(), "{f} should not be empty"); } } // ── scaffold_workspace: AGENTS.md references on-demand memory - #[test] - fn agents_md_references_on_demand_memory() { + #[tokio::test] + async fn agents_md_references_on_demand_memory() { let tmp = TempDir::new().unwrap(); let ctx = ProjectContext::default(); scaffold_workspace(tmp.path(), &ctx).unwrap(); - let agents = fs::read_to_string(tmp.path().join("AGENTS.md")).unwrap(); + let agents = tokio::fs::read_to_string(tmp.path().join("AGENTS.md")) + .await + .unwrap(); assert!( agents.contains("memory_recall"), "AGENTS.md should reference memory_recall for on-demand access" @@ -4777,13 +4815,15 @@ mod tests { // ── scaffold_workspace: MEMORY.md warns about token cost ──── - #[test] - fn memory_md_warns_about_token_cost() { + #[tokio::test] + async fn memory_md_warns_about_token_cost() { let tmp = TempDir::new().unwrap(); let ctx = ProjectContext::default(); scaffold_workspace(tmp.path(), &ctx).unwrap(); - let memory = fs::read_to_string(tmp.path().join("MEMORY.md")).unwrap(); + let memory = tokio::fs::read_to_string(tmp.path().join("MEMORY.md")) + .await + .unwrap(); assert!( memory.contains("costs tokens"), "MEMORY.md should warn about token cost" @@ -4796,13 +4836,15 @@ mod tests { // ── scaffold_workspace: TOOLS.md lists memory_forget ──────── - #[test] - fn tools_md_lists_all_builtin_tools() { + #[tokio::test] + async fn tools_md_lists_all_builtin_tools() { let tmp = TempDir::new().unwrap(); let ctx = ProjectContext::default(); scaffold_workspace(tmp.path(), &ctx).unwrap(); - let tools = fs::read_to_string(tmp.path().join("TOOLS.md")).unwrap(); + let tools = tokio::fs::read_to_string(tmp.path().join("TOOLS.md")) + .await + .unwrap(); for tool in &[ "shell", "file_read", @@ -4826,13 +4868,15 @@ mod tests { ); } - #[test] - fn soul_md_includes_emoji_awareness_guidance() { + #[tokio::test] + async fn soul_md_includes_emoji_awareness_guidance() { let tmp = TempDir::new().unwrap(); let ctx = ProjectContext::default(); scaffold_workspace(tmp.path(), &ctx).unwrap(); - let soul = fs::read_to_string(tmp.path().join("SOUL.md")).unwrap(); + let soul = tokio::fs::read_to_string(tmp.path().join("SOUL.md")) + .await + .unwrap(); assert!( soul.contains("Use emojis naturally (0-2 max"), "SOUL.md should include emoji usage guidance" @@ -4845,8 +4889,8 @@ mod tests { // ── scaffold_workspace: special characters in names ───────── - #[test] - fn scaffold_handles_special_characters_in_names() { + #[tokio::test] + async fn scaffold_handles_special_characters_in_names() { let tmp = TempDir::new().unwrap(); let ctx = ProjectContext { user_name: "José María".into(), @@ -4856,17 +4900,21 @@ mod tests { }; scaffold_workspace(tmp.path(), &ctx).unwrap(); - let user_md = fs::read_to_string(tmp.path().join("USER.md")).unwrap(); + let user_md = tokio::fs::read_to_string(tmp.path().join("USER.md")) + .await + .unwrap(); assert!(user_md.contains("José María")); - let soul = fs::read_to_string(tmp.path().join("SOUL.md")).unwrap(); + let soul = tokio::fs::read_to_string(tmp.path().join("SOUL.md")) + .await + .unwrap(); assert!(soul.contains("ZeroClaw-v2")); } // ── scaffold_workspace: full personalization round-trip ───── - #[test] - fn scaffold_full_personalization() { + #[tokio::test] + async fn scaffold_full_personalization() { let tmp = TempDir::new().unwrap(); let ctx = ProjectContext { user_name: "Argenis".into(), @@ -4879,27 +4927,39 @@ mod tests { scaffold_workspace(tmp.path(), &ctx).unwrap(); // Verify every file got personalized - let identity = fs::read_to_string(tmp.path().join("IDENTITY.md")).unwrap(); + let identity = tokio::fs::read_to_string(tmp.path().join("IDENTITY.md")) + .await + .unwrap(); assert!(identity.contains("**Name:** Claw")); - let soul = fs::read_to_string(tmp.path().join("SOUL.md")).unwrap(); + let soul = tokio::fs::read_to_string(tmp.path().join("SOUL.md")) + .await + .unwrap(); assert!(soul.contains("You are **Claw**")); assert!(soul.contains("Be friendly, human, and conversational")); - let user_md = fs::read_to_string(tmp.path().join("USER.md")).unwrap(); + let user_md = tokio::fs::read_to_string(tmp.path().join("USER.md")) + .await + .unwrap(); assert!(user_md.contains("**Name:** Argenis")); assert!(user_md.contains("**Timezone:** US/Eastern")); assert!(user_md.contains("Be friendly, human, and conversational")); - let agents = fs::read_to_string(tmp.path().join("AGENTS.md")).unwrap(); + let agents = tokio::fs::read_to_string(tmp.path().join("AGENTS.md")) + .await + .unwrap(); assert!(agents.contains("Claw Personal Assistant")); - let bootstrap = fs::read_to_string(tmp.path().join("BOOTSTRAP.md")).unwrap(); + let bootstrap = tokio::fs::read_to_string(tmp.path().join("BOOTSTRAP.md")) + .await + .unwrap(); assert!(bootstrap.contains("**Argenis**")); assert!(bootstrap.contains("US/Eastern")); assert!(bootstrap.contains("Introduce yourself as Claw")); - let heartbeat = fs::read_to_string(tmp.path().join("HEARTBEAT.md")).unwrap(); + let heartbeat = tokio::fs::read_to_string(tmp.path().join("HEARTBEAT.md")) + .await + .unwrap(); assert!(heartbeat.contains("Claw")); } diff --git a/src/peripherals/arduino_upload.rs b/src/peripherals/arduino_upload.rs index e11b19f..57a4f61 100644 --- a/src/peripherals/arduino_upload.rs +++ b/src/peripherals/arduino_upload.rs @@ -75,7 +75,7 @@ impl Tool for ArduinoUploadTool { let sketch_dir = temp_dir.join(sketch_name); let ino_path = sketch_dir.join(format!("{}.ino", sketch_name)); - if let Err(e) = std::fs::create_dir_all(&sketch_dir) { + if let Err(e) = tokio::fs::create_dir_all(&sketch_dir).await { return Ok(ToolResult { success: false, output: format!("Failed to create sketch dir: {}", e), @@ -83,8 +83,8 @@ impl Tool for ArduinoUploadTool { }); } - if let Err(e) = std::fs::write(&ino_path, code) { - let _ = std::fs::remove_dir_all(&temp_dir); + if let Err(e) = tokio::fs::write(&ino_path, code).await { + let _ = tokio::fs::remove_dir_all(&temp_dir).await; return Ok(ToolResult { success: false, output: format!("Failed to write sketch: {}", e), @@ -103,7 +103,7 @@ impl Tool for ArduinoUploadTool { let compile_output = match compile { Ok(o) => o, Err(e) => { - let _ = std::fs::remove_dir_all(&temp_dir); + let _ = tokio::fs::remove_dir_all(&temp_dir).await; return Ok(ToolResult { success: false, output: format!("arduino-cli compile failed: {}", e), @@ -114,7 +114,7 @@ impl Tool for ArduinoUploadTool { if !compile_output.status.success() { let stderr = String::from_utf8_lossy(&compile_output.stderr); - let _ = std::fs::remove_dir_all(&temp_dir); + let _ = tokio::fs::remove_dir_all(&temp_dir).await; return Ok(ToolResult { success: false, output: format!("Compile failed:\n{}", stderr), @@ -130,7 +130,7 @@ impl Tool for ArduinoUploadTool { let upload_output = match upload { Ok(o) => o, Err(e) => { - let _ = std::fs::remove_dir_all(&temp_dir); + let _ = tokio::fs::remove_dir_all(&temp_dir).await; return Ok(ToolResult { success: false, output: format!("arduino-cli upload failed: {}", e), @@ -139,7 +139,7 @@ impl Tool for ArduinoUploadTool { } }; - let _ = std::fs::remove_dir_all(&temp_dir); + let _ = tokio::fs::remove_dir_all(&temp_dir).await; if !upload_output.status.success() { let stderr = String::from_utf8_lossy(&upload_output.stderr); diff --git a/src/providers/compatible.rs b/src/providers/compatible.rs index 074ee45..40e0d94 100644 --- a/src/providers/compatible.rs +++ b/src/providers/compatible.rs @@ -1054,7 +1054,10 @@ impl Provider for OpenAiCompatibleProvider { let url = self.chat_completions_url(); let response = self - .apply_auth_header(self.http_client().post(&url).json(&native_request), credential) + .apply_auth_header( + self.http_client().post(&url).json(&native_request), + credential, + ) .send() .await?; diff --git a/src/providers/reliable.rs b/src/providers/reliable.rs index 85f9019..82b7d83 100644 --- a/src/providers/reliable.rs +++ b/src/providers/reliable.rs @@ -45,14 +45,12 @@ fn is_non_retryable(err: &anyhow::Error) -> bool { return true; } - let model_catalog_mismatch = msg_lower.contains("model") + msg_lower.contains("model") && (msg_lower.contains("not found") || msg_lower.contains("unknown") || msg_lower.contains("unsupported") || msg_lower.contains("does not exist") - || msg_lower.contains("invalid")); - - model_catalog_mismatch + || msg_lower.contains("invalid")) } /// Check if an error is a rate-limit (429) error. diff --git a/src/security/audit.rs b/src/security/audit.rs index 80c45cb..816ecc7 100644 --- a/src/security/audit.rs +++ b/src/security/audit.rs @@ -335,8 +335,8 @@ mod tests { // ── §8.1 Log rotation tests ───────────────────────────── - #[test] - fn audit_logger_writes_event_when_enabled() -> Result<()> { + #[tokio::test] + async fn audit_logger_writes_event_when_enabled() -> Result<()> { let tmp = TempDir::new()?; let config = AuditConfig { enabled: true, @@ -353,7 +353,7 @@ mod tests { let log_path = tmp.path().join("audit.log"); assert!(log_path.exists(), "audit log file must be created"); - let content = std::fs::read_to_string(&log_path)?; + let content = tokio::fs::read_to_string(&log_path).await?; assert!(!content.is_empty(), "audit log must not be empty"); let parsed: AuditEvent = serde_json::from_str(content.trim())?; @@ -361,8 +361,8 @@ mod tests { Ok(()) } - #[test] - fn audit_log_command_event_writes_structured_entry() -> Result<()> { + #[tokio::test] + async fn audit_log_command_event_writes_structured_entry() -> Result<()> { let tmp = TempDir::new()?; let config = AuditConfig { enabled: true, @@ -382,7 +382,7 @@ mod tests { })?; let log_path = tmp.path().join("audit.log"); - let content = std::fs::read_to_string(&log_path)?; + let content = tokio::fs::read_to_string(&log_path).await?; let parsed: AuditEvent = serde_json::from_str(content.trim())?; let action = parsed.action.unwrap(); diff --git a/src/security/secrets.rs b/src/security/secrets.rs index 2a26831..663112c 100644 --- a/src/security/secrets.rs +++ b/src/security/secrets.rs @@ -334,8 +334,8 @@ mod tests { assert!(!SecretStore::is_encrypted("")); } - #[test] - fn key_file_created_on_first_encrypt() { + #[tokio::test] + async fn key_file_created_on_first_encrypt() { let tmp = TempDir::new().unwrap(); let store = SecretStore::new(tmp.path(), true); assert!(!store.key_path.exists()); @@ -343,7 +343,7 @@ mod tests { store.encrypt("test").unwrap(); assert!(store.key_path.exists(), "Key file should be created"); - let key_hex = fs::read_to_string(&store.key_path).unwrap(); + let key_hex = tokio::fs::read_to_string(&store.key_path).await.unwrap(); assert_eq!( key_hex.len(), KEY_LEN * 2, diff --git a/src/skillforge/integrate.rs b/src/skillforge/integrate.rs index 540dd8b..6535d59 100644 --- a/src/skillforge/integrate.rs +++ b/src/skillforge/integrate.rs @@ -191,8 +191,8 @@ mod tests { } } - #[test] - fn integrate_creates_files() { + #[tokio::test] + async fn integrate_creates_files() { let tmp = std::env::temp_dir().join("zeroclaw-test-integrate"); let _ = fs::remove_dir_all(&tmp); @@ -203,11 +203,15 @@ mod tests { assert!(path.join("SKILL.toml").exists()); assert!(path.join("SKILL.md").exists()); - let toml = fs::read_to_string(path.join("SKILL.toml")).unwrap(); + let toml = tokio::fs::read_to_string(path.join("SKILL.toml")) + .await + .unwrap(); assert!(toml.contains("name = \"test-skill\"")); assert!(toml.contains("stars = 42")); - let md = fs::read_to_string(path.join("SKILL.md")).unwrap(); + let md = tokio::fs::read_to_string(path.join("SKILL.md")) + .await + .unwrap(); assert!(md.contains("# test-skill")); assert!(md.contains("A test skill for unit tests")); diff --git a/src/skills/symlink_tests.rs b/src/skills/symlink_tests.rs index c77393a..fa2c046 100644 --- a/src/skills/symlink_tests.rs +++ b/src/skills/symlink_tests.rs @@ -4,21 +4,21 @@ mod tests { use std::path::Path; use tempfile::TempDir; - #[test] - fn test_skills_symlink_unix_edge_cases() { + #[tokio::test] + async fn test_skills_symlink_unix_edge_cases() { let tmp = TempDir::new().unwrap(); let workspace_dir = tmp.path().join("workspace"); - std::fs::create_dir_all(&workspace_dir).unwrap(); + tokio::fs::create_dir_all(&workspace_dir).await.unwrap(); let skills_path = skills_dir(&workspace_dir); - std::fs::create_dir_all(&skills_path).unwrap(); + tokio::fs::create_dir_all(&skills_path).await.unwrap(); // Test case 1: Valid symlink creation on Unix #[cfg(unix)] { let source_dir = tmp.path().join("source_skill"); - std::fs::create_dir_all(&source_dir).unwrap(); - std::fs::write(source_dir.join("SKILL.md"), "# Test Skill\nContent").unwrap(); + tokio::fs::create_dir_all(&source_dir).await.unwrap(); + tokio::fs::write(source_dir.join("SKILL.md"), "# Test Skill\nContent").await.unwrap(); let dest_link = skills_path.join("linked_skill"); @@ -31,7 +31,7 @@ mod tests { assert!(dest_link.is_symlink()); // Verify we can read through symlink - let content = std::fs::read_to_string(dest_link.join("SKILL.md")); + let content = tokio::fs::read_to_string(dest_link.join("SKILL.md")).await; assert!(content.is_ok()); assert!(content.unwrap().contains("Test Skill")); @@ -45,7 +45,7 @@ mod tests { ); // But reading through it should fail - let content = std::fs::read_to_string(broken_link.join("SKILL.md")); + let content = tokio::fs::read_to_string(broken_link.join("SKILL.md")).await; assert!(content.is_err()); } @@ -53,7 +53,7 @@ mod tests { #[cfg(windows)] { let source_dir = tmp.path().join("source_skill"); - std::fs::create_dir_all(&source_dir).unwrap(); + tokio::fs::create_dir_all(&source_dir).await.unwrap(); let dest_link = skills_path.join("linked_skill"); @@ -64,7 +64,7 @@ mod tests { assert!(!dest_link.exists()); } else { // Clean up if it succeeded - let _ = std::fs::remove_dir(&dest_link); + let _ = tokio::fs::remove_dir(&dest_link).await; } } @@ -80,21 +80,21 @@ mod tests { assert!(!empty_skills_path.exists()); } - #[test] - fn test_skills_symlink_permissions_and_safety() { + #[tokio::test] + async fn test_skills_symlink_permissions_and_safety() { let tmp = TempDir::new().unwrap(); let workspace_dir = tmp.path().join("workspace"); - std::fs::create_dir_all(&workspace_dir).unwrap(); + tokio::fs::create_dir_all(&workspace_dir).await.unwrap(); let skills_path = skills_dir(&workspace_dir); - std::fs::create_dir_all(&skills_path).unwrap(); + tokio::fs::create_dir_all(&skills_path).await.unwrap(); #[cfg(unix)] { // Test case: Symlink outside workspace should be allowed (user responsibility) let outside_dir = tmp.path().join("outside_skill"); - std::fs::create_dir_all(&outside_dir).unwrap(); - std::fs::write(outside_dir.join("SKILL.md"), "# Outside Skill\nContent").unwrap(); + tokio::fs::create_dir_all(&outside_dir).await.unwrap(); + tokio::fs::write(outside_dir.join("SKILL.md"), "# Outside Skill\nContent").await.unwrap(); let dest_link = skills_path.join("outside_skill"); let result = std::os::unix::fs::symlink(&outside_dir, &dest_link); @@ -104,7 +104,7 @@ mod tests { ); // Should still be readable - let content = std::fs::read_to_string(dest_link.join("SKILL.md")); + let content = tokio::fs::read_to_string(dest_link.join("SKILL.md")).await; assert!(content.is_ok()); assert!(content.unwrap().contains("Outside Skill")); } diff --git a/src/tools/browser.rs b/src/tools/browser.rs index 36914a6..2c057ca 100644 --- a/src/tools/browser.rs +++ b/src/tools/browser.rs @@ -1225,8 +1225,9 @@ mod native_backend { }); if let Some(path_str) = path { - std::fs::write(&path_str, &png) - .with_context(|| format!("Failed to write screenshot to {path_str}"))?; + tokio::fs::write(&path_str, &png).await.with_context(|| { + format!("Failed to write screenshot to {path_str}") + })?; payload["path"] = Value::String(path_str); } else { payload["png_base64"] = diff --git a/src/tools/cron_add.rs b/src/tools/cron_add.rs index bd3abea..fda5edf 100644 --- a/src/tools/cron_add.rs +++ b/src/tools/cron_add.rs @@ -217,13 +217,13 @@ mod tests { use crate::security::AutonomyLevel; use tempfile::TempDir; - fn test_config(tmp: &TempDir) -> Arc { + async fn test_config(tmp: &TempDir) -> Arc { let config = Config { workspace_dir: tmp.path().join("workspace"), config_path: tmp.path().join("config.toml"), ..Config::default() }; - std::fs::create_dir_all(&config.workspace_dir).unwrap(); + tokio::fs::create_dir_all(&config.workspace_dir).await.unwrap(); Arc::new(config) } @@ -237,7 +237,7 @@ mod tests { #[tokio::test] async fn adds_shell_job() { let tmp = TempDir::new().unwrap(); - let cfg = test_config(&tmp); + let cfg = test_config(&tmp).await; let tool = CronAddTool::new(cfg.clone(), test_security(&cfg)); let result = tool .execute(json!({ @@ -262,7 +262,7 @@ mod tests { }; config.autonomy.allowed_commands = vec!["echo".into()]; config.autonomy.level = AutonomyLevel::Supervised; - std::fs::create_dir_all(&config.workspace_dir).unwrap(); + tokio::fs::create_dir_all(&config.workspace_dir).await.unwrap(); let cfg = Arc::new(config); let tool = CronAddTool::new(cfg.clone(), test_security(&cfg)); @@ -285,7 +285,7 @@ mod tests { #[tokio::test] async fn rejects_invalid_schedule() { let tmp = TempDir::new().unwrap(); - let cfg = test_config(&tmp); + let cfg = test_config(&tmp).await; let tool = CronAddTool::new(cfg.clone(), test_security(&cfg)); let result = tool @@ -307,7 +307,7 @@ mod tests { #[tokio::test] async fn agent_job_requires_prompt() { let tmp = TempDir::new().unwrap(); - let cfg = test_config(&tmp); + let cfg = test_config(&tmp).await; let tool = CronAddTool::new(cfg.clone(), test_security(&cfg)); let result = tool diff --git a/src/tools/cron_list.rs b/src/tools/cron_list.rs index 0392370..6ae1ac3 100644 --- a/src/tools/cron_list.rs +++ b/src/tools/cron_list.rs @@ -63,20 +63,20 @@ mod tests { use crate::config::Config; use tempfile::TempDir; - fn test_config(tmp: &TempDir) -> Arc { + async fn test_config(tmp: &TempDir) -> Arc { let config = Config { workspace_dir: tmp.path().join("workspace"), config_path: tmp.path().join("config.toml"), ..Config::default() }; - std::fs::create_dir_all(&config.workspace_dir).unwrap(); + tokio::fs::create_dir_all(&config.workspace_dir).await.unwrap(); Arc::new(config) } #[tokio::test] async fn returns_empty_list_when_no_jobs() { let tmp = TempDir::new().unwrap(); - let cfg = test_config(&tmp); + let cfg = test_config(&tmp).await; let tool = CronListTool::new(cfg); let result = tool.execute(json!({})).await.unwrap(); @@ -87,7 +87,7 @@ mod tests { #[tokio::test] async fn errors_when_cron_disabled() { let tmp = TempDir::new().unwrap(); - let mut cfg = (*test_config(&tmp)).clone(); + let mut cfg = (*test_config(&tmp).await).clone(); cfg.cron.enabled = false; let tool = CronListTool::new(Arc::new(cfg)); diff --git a/src/tools/cron_remove.rs b/src/tools/cron_remove.rs index 01a70dc..e19edab 100644 --- a/src/tools/cron_remove.rs +++ b/src/tools/cron_remove.rs @@ -76,20 +76,20 @@ mod tests { use crate::config::Config; use tempfile::TempDir; - fn test_config(tmp: &TempDir) -> Arc { + async fn test_config(tmp: &TempDir) -> Arc { let config = Config { workspace_dir: tmp.path().join("workspace"), config_path: tmp.path().join("config.toml"), ..Config::default() }; - std::fs::create_dir_all(&config.workspace_dir).unwrap(); + tokio::fs::create_dir_all(&config.workspace_dir).await.unwrap(); Arc::new(config) } #[tokio::test] async fn removes_existing_job() { let tmp = TempDir::new().unwrap(); - let cfg = test_config(&tmp); + let cfg = test_config(&tmp).await; let job = cron::add_job(&cfg, "*/5 * * * *", "echo ok").unwrap(); let tool = CronRemoveTool::new(cfg.clone()); @@ -101,7 +101,7 @@ mod tests { #[tokio::test] async fn errors_when_job_id_missing() { let tmp = TempDir::new().unwrap(); - let cfg = test_config(&tmp); + let cfg = test_config(&tmp).await; let tool = CronRemoveTool::new(cfg); let result = tool.execute(json!({})).await.unwrap(); diff --git a/src/tools/cron_run.rs b/src/tools/cron_run.rs index a4e5f75..edfe402 100644 --- a/src/tools/cron_run.rs +++ b/src/tools/cron_run.rs @@ -107,20 +107,20 @@ mod tests { use crate::config::Config; use tempfile::TempDir; - fn test_config(tmp: &TempDir) -> Arc { + async fn test_config(tmp: &TempDir) -> Arc { let config = Config { workspace_dir: tmp.path().join("workspace"), config_path: tmp.path().join("config.toml"), ..Config::default() }; - std::fs::create_dir_all(&config.workspace_dir).unwrap(); + tokio::fs::create_dir_all(&config.workspace_dir).await.unwrap(); Arc::new(config) } #[tokio::test] async fn force_runs_job_and_records_history() { let tmp = TempDir::new().unwrap(); - let cfg = test_config(&tmp); + let cfg = test_config(&tmp).await; let job = cron::add_job(&cfg, "*/5 * * * *", "echo run-now").unwrap(); let tool = CronRunTool::new(cfg.clone()); @@ -134,7 +134,7 @@ mod tests { #[tokio::test] async fn errors_for_missing_job() { let tmp = TempDir::new().unwrap(); - let cfg = test_config(&tmp); + let cfg = test_config(&tmp).await; let tool = CronRunTool::new(cfg); let result = tool diff --git a/src/tools/cron_runs.rs b/src/tools/cron_runs.rs index 280baa1..eb5fdfb 100644 --- a/src/tools/cron_runs.rs +++ b/src/tools/cron_runs.rs @@ -121,20 +121,20 @@ mod tests { use chrono::{Duration as ChronoDuration, Utc}; use tempfile::TempDir; - fn test_config(tmp: &TempDir) -> Arc { + async fn test_config(tmp: &TempDir) -> Arc { let config = Config { workspace_dir: tmp.path().join("workspace"), config_path: tmp.path().join("config.toml"), ..Config::default() }; - std::fs::create_dir_all(&config.workspace_dir).unwrap(); + tokio::fs::create_dir_all(&config.workspace_dir).await.unwrap(); Arc::new(config) } #[tokio::test] async fn lists_runs_with_truncation() { let tmp = TempDir::new().unwrap(); - let cfg = test_config(&tmp); + let cfg = test_config(&tmp).await; let job = cron::add_job(&cfg, "*/5 * * * *", "echo ok").unwrap(); let long_output = "x".repeat(1000); @@ -163,7 +163,7 @@ mod tests { #[tokio::test] async fn errors_when_job_id_missing() { let tmp = TempDir::new().unwrap(); - let cfg = test_config(&tmp); + let cfg = test_config(&tmp).await; let tool = CronRunsTool::new(cfg); let result = tool.execute(json!({})).await.unwrap(); assert!(!result.success); diff --git a/src/tools/cron_update.rs b/src/tools/cron_update.rs index c224b17..26ca21e 100644 --- a/src/tools/cron_update.rs +++ b/src/tools/cron_update.rs @@ -111,13 +111,13 @@ mod tests { use crate::config::Config; use tempfile::TempDir; - fn test_config(tmp: &TempDir) -> Arc { + async fn test_config(tmp: &TempDir) -> Arc { let config = Config { workspace_dir: tmp.path().join("workspace"), config_path: tmp.path().join("config.toml"), ..Config::default() }; - std::fs::create_dir_all(&config.workspace_dir).unwrap(); + tokio::fs::create_dir_all(&config.workspace_dir).await.unwrap(); Arc::new(config) } @@ -131,7 +131,7 @@ mod tests { #[tokio::test] async fn updates_enabled_flag() { let tmp = TempDir::new().unwrap(); - let cfg = test_config(&tmp); + let cfg = test_config(&tmp).await; let job = cron::add_job(&cfg, "*/5 * * * *", "echo ok").unwrap(); let tool = CronUpdateTool::new(cfg.clone(), test_security(&cfg)); @@ -156,7 +156,7 @@ mod tests { ..Config::default() }; config.autonomy.allowed_commands = vec!["echo".into()]; - std::fs::create_dir_all(&config.workspace_dir).unwrap(); + tokio::fs::create_dir_all(&config.workspace_dir).await.unwrap(); let cfg = Arc::new(config); let job = cron::add_job(&cfg, "*/5 * * * *", "echo ok").unwrap(); let tool = CronUpdateTool::new(cfg.clone(), test_security(&cfg)); diff --git a/src/tools/image_info.rs b/src/tools/image_info.rs index 349f707..558fbb7 100644 --- a/src/tools/image_info.rs +++ b/src/tools/image_info.rs @@ -428,7 +428,7 @@ mod tests { async fn execute_real_file() { // Create a minimal valid PNG let dir = std::env::temp_dir().join("zeroclaw_image_info_test"); - let _ = std::fs::create_dir_all(&dir); + let _ = tokio::fs::create_dir_all(&dir).await; let png_path = dir.join("test.png"); // Minimal 1x1 red PNG (67 bytes) @@ -448,7 +448,7 @@ mod tests { 0x49, 0x45, 0x4E, 0x44, // IEND 0xAE, 0x42, 0x60, 0x82, // CRC ]; - std::fs::write(&png_path, &png_bytes).unwrap(); + tokio::fs::write(&png_path, &png_bytes).await.unwrap(); let tool = ImageInfoTool::new(test_security()); let result = tool @@ -461,13 +461,13 @@ mod tests { assert!(!result.output.contains("data:")); // Clean up - let _ = std::fs::remove_dir_all(&dir); + let _ = tokio::fs::remove_dir_all(&dir).await; } #[tokio::test] async fn execute_with_base64() { let dir = std::env::temp_dir().join("zeroclaw_image_info_b64"); - let _ = std::fs::create_dir_all(&dir); + let _ = tokio::fs::create_dir_all(&dir).await; let png_path = dir.join("test_b64.png"); // Minimal 1x1 PNG @@ -478,7 +478,7 @@ mod tests { 0xD7, 0x63, 0xF8, 0xCF, 0xC0, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0xE2, 0x21, 0xBC, 0x33, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82, ]; - std::fs::write(&png_path, &png_bytes).unwrap(); + tokio::fs::write(&png_path, &png_bytes).await.unwrap(); let tool = ImageInfoTool::new(test_security()); let result = tool @@ -488,6 +488,6 @@ mod tests { assert!(result.success); assert!(result.output.contains("data:image/png;base64,")); - let _ = std::fs::remove_dir_all(&dir); + let _ = tokio::fs::remove_dir_all(&dir).await; } } diff --git a/src/tools/pushover.rs b/src/tools/pushover.rs index 308c2ff..7e64e9a 100644 --- a/src/tools/pushover.rs +++ b/src/tools/pushover.rs @@ -41,9 +41,10 @@ impl PushoverTool { ) } - fn get_credentials(&self) -> anyhow::Result<(String, String)> { + async fn get_credentials(&self) -> anyhow::Result<(String, String)> { let env_path = self.workspace_dir.join(".env"); - let content = std::fs::read_to_string(&env_path) + let content = tokio::fs::read_to_string(&env_path) + .await .map_err(|e| anyhow::anyhow!("Failed to read {}: {}", env_path.display(), e))?; let mut token = None; @@ -153,7 +154,7 @@ impl Tool for PushoverTool { let sound = args.get("sound").and_then(|v| v.as_str()).map(String::from); - let (token, user_key) = self.get_credentials()?; + let (token, user_key) = self.get_credentials().await?; let mut form = reqwest::multipart::Form::new() .text("token", token) @@ -269,8 +270,8 @@ mod tests { assert!(required.contains(&serde_json::Value::String("message".to_string()))); } - #[test] - fn credentials_parsed_from_env_file() { + #[tokio::test] + async fn credentials_parsed_from_env_file() { let tmp = TempDir::new().unwrap(); let env_path = tmp.path().join(".env"); fs::write( @@ -283,7 +284,7 @@ mod tests { test_security(AutonomyLevel::Full, 100), tmp.path().to_path_buf(), ); - let result = tool.get_credentials(); + let result = tool.get_credentials().await; assert!(result.is_ok()); let (token, user_key) = result.unwrap(); @@ -291,20 +292,20 @@ mod tests { assert_eq!(user_key, "userkey456"); } - #[test] - fn credentials_fail_without_env_file() { + #[tokio::test] + async fn credentials_fail_without_env_file() { let tmp = TempDir::new().unwrap(); let tool = PushoverTool::new( test_security(AutonomyLevel::Full, 100), tmp.path().to_path_buf(), ); - let result = tool.get_credentials(); + let result = tool.get_credentials().await; assert!(result.is_err()); } - #[test] - fn credentials_fail_without_token() { + #[tokio::test] + async fn credentials_fail_without_token() { let tmp = TempDir::new().unwrap(); let env_path = tmp.path().join(".env"); fs::write(&env_path, "PUSHOVER_USER_KEY=userkey456\n").unwrap(); @@ -313,13 +314,13 @@ mod tests { test_security(AutonomyLevel::Full, 100), tmp.path().to_path_buf(), ); - let result = tool.get_credentials(); + let result = tool.get_credentials().await; assert!(result.is_err()); } - #[test] - fn credentials_fail_without_user_key() { + #[tokio::test] + async fn credentials_fail_without_user_key() { let tmp = TempDir::new().unwrap(); let env_path = tmp.path().join(".env"); fs::write(&env_path, "PUSHOVER_TOKEN=testtoken123\n").unwrap(); @@ -328,13 +329,13 @@ mod tests { test_security(AutonomyLevel::Full, 100), tmp.path().to_path_buf(), ); - let result = tool.get_credentials(); + let result = tool.get_credentials().await; assert!(result.is_err()); } - #[test] - fn credentials_ignore_comments() { + #[tokio::test] + async fn credentials_ignore_comments() { let tmp = TempDir::new().unwrap(); let env_path = tmp.path().join(".env"); fs::write(&env_path, "# This is a comment\nPUSHOVER_TOKEN=realtoken\n# Another comment\nPUSHOVER_USER_KEY=realuser\n").unwrap(); @@ -343,7 +344,7 @@ mod tests { test_security(AutonomyLevel::Full, 100), tmp.path().to_path_buf(), ); - let result = tool.get_credentials(); + let result = tool.get_credentials().await; assert!(result.is_ok()); let (token, user_key) = result.unwrap(); @@ -371,8 +372,8 @@ mod tests { assert!(schema["properties"].get("sound").is_some()); } - #[test] - fn credentials_support_export_and_quoted_values() { + #[tokio::test] + async fn credentials_support_export_and_quoted_values() { let tmp = TempDir::new().unwrap(); let env_path = tmp.path().join(".env"); fs::write( @@ -385,7 +386,7 @@ mod tests { test_security(AutonomyLevel::Full, 100), tmp.path().to_path_buf(), ); - let result = tool.get_credentials(); + let result = tool.get_credentials().await; assert!(result.is_ok()); let (token, user_key) = result.unwrap(); diff --git a/src/tools/schedule.rs b/src/tools/schedule.rs index 96c3023..9270e1c 100644 --- a/src/tools/schedule.rs +++ b/src/tools/schedule.rs @@ -368,14 +368,14 @@ mod tests { use crate::security::AutonomyLevel; use tempfile::TempDir; - fn test_setup() -> (TempDir, Config, Arc) { + async fn test_setup() -> (TempDir, Config, Arc) { let tmp = TempDir::new().unwrap(); let config = Config { workspace_dir: tmp.path().join("workspace"), config_path: tmp.path().join("config.toml"), ..Config::default() }; - std::fs::create_dir_all(&config.workspace_dir).unwrap(); + tokio::fs::create_dir_all(&config.workspace_dir).await.unwrap(); let security = Arc::new(SecurityPolicy::from_config( &config.autonomy, &config.workspace_dir, @@ -383,9 +383,9 @@ mod tests { (tmp, config, security) } - #[test] - fn tool_name_and_schema() { - let (_tmp, config, security) = test_setup(); + #[tokio::test] + async fn tool_name_and_schema() { + let (_tmp, config, security) = test_setup().await; let tool = ScheduleTool::new(security, config); assert_eq!(tool.name(), "schedule"); let schema = tool.parameters_schema(); @@ -394,7 +394,7 @@ mod tests { #[tokio::test] async fn list_empty() { - let (_tmp, config, security) = test_setup(); + let (_tmp, config, security) = test_setup().await; let tool = ScheduleTool::new(security, config); let result = tool.execute(json!({"action": "list"})).await.unwrap(); @@ -404,7 +404,7 @@ mod tests { #[tokio::test] async fn create_get_and_cancel_roundtrip() { - let (_tmp, config, security) = test_setup(); + let (_tmp, config, security) = test_setup().await; let tool = ScheduleTool::new(security, config); let create = tool @@ -440,7 +440,7 @@ mod tests { #[tokio::test] async fn once_and_pause_resume_aliases_work() { - let (_tmp, config, security) = test_setup(); + let (_tmp, config, security) = test_setup().await; let tool = ScheduleTool::new(security, config); let once = tool @@ -489,7 +489,7 @@ mod tests { }, ..Config::default() }; - std::fs::create_dir_all(&config.workspace_dir).unwrap(); + tokio::fs::create_dir_all(&config.workspace_dir).await.unwrap(); let security = Arc::new(SecurityPolicy::from_config( &config.autonomy, &config.workspace_dir, @@ -514,7 +514,7 @@ mod tests { #[tokio::test] async fn unknown_action_returns_failure() { - let (_tmp, config, security) = test_setup(); + let (_tmp, config, security) = test_setup().await; let tool = ScheduleTool::new(security, config); let result = tool.execute(json!({"action": "explode"})).await.unwrap(); diff --git a/src/tools/shell.rs b/src/tools/shell.rs index 031ed4b..9574cd5 100644 --- a/src/tools/shell.rs +++ b/src/tools/shell.rs @@ -363,7 +363,7 @@ mod tests { .unwrap(); assert!(allowed.success); - let _ = std::fs::remove_file(std::env::temp_dir().join("zeroclaw_shell_approval_test")); + let _ = tokio::fs::remove_file(std::env::temp_dir().join("zeroclaw_shell_approval_test")).await; } // ── §5.2 Shell timeout enforcement tests ───────────────── diff --git a/tests/dockerignore_test.rs b/tests/dockerignore_test.rs index f321753..8af6fa8 100644 --- a/tests/dockerignore_test.rs +++ b/tests/dockerignore_test.rs @@ -6,7 +6,6 @@ //! 3. All build-essential paths are NOT excluded //! 4. Pattern syntax is valid -use std::fs; use std::path::Path; /// Paths that MUST be excluded from Docker build context (security/performance) @@ -96,8 +95,8 @@ fn is_excluded(patterns: &[String], path: &str) -> bool { excluded } -#[test] -fn dockerignore_file_exists() { +#[tokio::test] +async fn dockerignore_file_exists() { let path = Path::new(env!("CARGO_MANIFEST_DIR")).join(".dockerignore"); assert!( path.exists(), @@ -105,10 +104,12 @@ fn dockerignore_file_exists() { ); } -#[test] -fn dockerignore_excludes_security_critical_paths() { +#[tokio::test] +async fn dockerignore_excludes_security_critical_paths() { let path = Path::new(env!("CARGO_MANIFEST_DIR")).join(".dockerignore"); - let content = fs::read_to_string(&path).expect("Failed to read .dockerignore"); + let content = tokio::fs::read_to_string(&path) + .await + .expect("Failed to read .dockerignore"); let patterns = parse_dockerignore(&content); for must_exclude in MUST_EXCLUDE { @@ -129,10 +130,12 @@ fn dockerignore_excludes_security_critical_paths() { } } -#[test] -fn dockerignore_does_not_exclude_build_essentials() { +#[tokio::test] +async fn dockerignore_does_not_exclude_build_essentials() { let path = Path::new(env!("CARGO_MANIFEST_DIR")).join(".dockerignore"); - let content = fs::read_to_string(&path).expect("Failed to read .dockerignore"); + let content = tokio::fs::read_to_string(&path) + .await + .expect("Failed to read .dockerignore"); let patterns = parse_dockerignore(&content); for must_include in MUST_INCLUDE { @@ -144,10 +147,12 @@ fn dockerignore_does_not_exclude_build_essentials() { } } -#[test] -fn dockerignore_excludes_git_directory() { +#[tokio::test] +async fn dockerignore_excludes_git_directory() { let path = Path::new(env!("CARGO_MANIFEST_DIR")).join(".dockerignore"); - let content = fs::read_to_string(&path).expect("Failed to read .dockerignore"); + let content = tokio::fs::read_to_string(&path) + .await + .expect("Failed to read .dockerignore"); let patterns = parse_dockerignore(&content); // .git directory and its contents must be excluded @@ -162,10 +167,12 @@ fn dockerignore_excludes_git_directory() { ); } -#[test] -fn dockerignore_excludes_target_directory() { +#[tokio::test] +async fn dockerignore_excludes_target_directory() { let path = Path::new(env!("CARGO_MANIFEST_DIR")).join(".dockerignore"); - let content = fs::read_to_string(&path).expect("Failed to read .dockerignore"); + let content = tokio::fs::read_to_string(&path) + .await + .expect("Failed to read .dockerignore"); let patterns = parse_dockerignore(&content); assert!(is_excluded(&patterns, "target"), "target must be excluded"); @@ -179,10 +186,12 @@ fn dockerignore_excludes_target_directory() { ); } -#[test] -fn dockerignore_excludes_database_files() { +#[tokio::test] +async fn dockerignore_excludes_database_files() { let path = Path::new(env!("CARGO_MANIFEST_DIR")).join(".dockerignore"); - let content = fs::read_to_string(&path).expect("Failed to read .dockerignore"); + let content = tokio::fs::read_to_string(&path) + .await + .expect("Failed to read .dockerignore"); let patterns = parse_dockerignore(&content); assert!( @@ -199,10 +208,12 @@ fn dockerignore_excludes_database_files() { ); } -#[test] -fn dockerignore_excludes_markdown_files() { +#[tokio::test] +async fn dockerignore_excludes_markdown_files() { let path = Path::new(env!("CARGO_MANIFEST_DIR")).join(".dockerignore"); - let content = fs::read_to_string(&path).expect("Failed to read .dockerignore"); + let content = tokio::fs::read_to_string(&path) + .await + .expect("Failed to read .dockerignore"); let patterns = parse_dockerignore(&content); assert!( @@ -219,10 +230,12 @@ fn dockerignore_excludes_markdown_files() { ); } -#[test] -fn dockerignore_excludes_image_files() { +#[tokio::test] +async fn dockerignore_excludes_image_files() { let path = Path::new(env!("CARGO_MANIFEST_DIR")).join(".dockerignore"); - let content = fs::read_to_string(&path).expect("Failed to read .dockerignore"); + let content = tokio::fs::read_to_string(&path) + .await + .expect("Failed to read .dockerignore"); let patterns = parse_dockerignore(&content); assert!( @@ -235,10 +248,12 @@ fn dockerignore_excludes_image_files() { ); } -#[test] -fn dockerignore_excludes_env_files() { +#[tokio::test] +async fn dockerignore_excludes_env_files() { let path = Path::new(env!("CARGO_MANIFEST_DIR")).join(".dockerignore"); - let content = fs::read_to_string(&path).expect("Failed to read .dockerignore"); + let content = tokio::fs::read_to_string(&path) + .await + .expect("Failed to read .dockerignore"); let patterns = parse_dockerignore(&content); assert!( @@ -247,10 +262,12 @@ fn dockerignore_excludes_env_files() { ); } -#[test] -fn dockerignore_excludes_ci_configs() { +#[tokio::test] +async fn dockerignore_excludes_ci_configs() { let path = Path::new(env!("CARGO_MANIFEST_DIR")).join(".dockerignore"); - let content = fs::read_to_string(&path).expect("Failed to read .dockerignore"); + let content = tokio::fs::read_to_string(&path) + .await + .expect("Failed to read .dockerignore"); let patterns = parse_dockerignore(&content); assert!( @@ -263,10 +280,12 @@ fn dockerignore_excludes_ci_configs() { ); } -#[test] -fn dockerignore_has_valid_syntax() { +#[tokio::test] +async fn dockerignore_has_valid_syntax() { let path = Path::new(env!("CARGO_MANIFEST_DIR")).join(".dockerignore"); - let content = fs::read_to_string(&path).expect("Failed to read .dockerignore"); + let content = tokio::fs::read_to_string(&path) + .await + .expect("Failed to read .dockerignore"); for (line_num, line) in content.lines().enumerate() { let trimmed = line.trim(); @@ -294,8 +313,8 @@ fn dockerignore_has_valid_syntax() { } } -#[test] -fn dockerignore_pattern_matching_edge_cases() { +#[tokio::test] +async fn dockerignore_pattern_matching_edge_cases() { // Test the pattern matching logic itself let patterns = vec![ ".git".to_string(),