aether_project/
testing.rs1use std::fs;
7use std::path::{Path, PathBuf};
8
9use tempfile::TempDir;
10
11use crate::{AgentConfig, PromptSource, SKILL_FILENAME};
12use aether_core::agent_spec::{AgentSpec, AgentSpecExposure};
13use llm::{ModelSettings, ProviderConnectionOverrides};
14use mcp_utils::client::ToolFilter;
15
16pub fn project() -> TestProject {
18 TestProject { root: temp_dir("project") }
19}
20
21pub fn home() -> TestHome {
23 TestHome { root: temp_dir("home") }
24}
25
26pub fn settings_agent(name: &str, description: &str) -> AgentConfig {
28 AgentConfig {
29 name: name.to_string(),
30 description: description.to_string(),
31 model: "anthropic:claude-sonnet-4-5".to_string(),
32 user_invocable: true,
33 ..AgentConfig::default()
34 }
35}
36
37pub fn agent_config(name: &str) -> AgentConfig {
39 AgentConfig { prompts: vec![PromptSource::file("PROMPT.md")], ..settings_agent(name, &format!("{name} agent")) }
40}
41
42pub fn fake_spec(name: &str, exposure: AgentSpecExposure) -> AgentSpec {
44 AgentSpec {
45 name: name.to_string(),
46 description: format!("{name} agent"),
47 model: "anthropic:claude-sonnet-4-5".to_string(),
48 reasoning_effort: None,
49 model_settings: ModelSettings::default(),
50 context_window: None,
51 prompts: vec![],
52 provider_connections: ProviderConnectionOverrides::default(),
53 mcp_config_sources: Vec::new(),
54 exposure,
55 tools: ToolFilter::default(),
56 }
57}
58
59pub struct TestProject {
61 root: TempDir,
62}
63
64impl TestProject {
65 pub fn root(&self) -> &Path {
67 self.root.path()
68 }
69
70 pub fn file(self, path: &str, content: &str) -> Self {
72 self.write(path, content);
73 self
74 }
75
76 pub fn skill(self, name: &str, content: &str) -> Self {
78 self.file(&format!("{name}/{SKILL_FILENAME}"), content)
79 }
80
81 pub fn write(&self, path: &str, content: &str) {
83 write_file(self.root.path(), path, content);
84 }
85}
86
87pub struct TestHome {
89 root: TempDir,
90}
91
92impl TestHome {
93 pub fn aether(&self) -> PathBuf {
95 self.root.path().join(".aether")
96 }
97
98 pub fn root(&self) -> &Path {
100 self.root.path()
101 }
102
103 pub fn file(self, path: &str, content: &str) -> Self {
105 write_file(self.root.path(), path, content);
106 self
107 }
108
109 pub fn settings(self, json: &str) -> Self {
111 self.file(".aether/settings.json", json)
112 }
113}
114
115fn temp_dir(label: &str) -> TempDir {
116 TempDir::new().unwrap_or_else(|error| panic!("failed to create temp {label} dir: {error}"))
117}
118
119fn write_file(root: &Path, path: &str, content: &str) {
120 let full_path = root.join(path);
121 if let Some(parent) = full_path.parent() {
122 fs::create_dir_all(parent).expect("failed to create fixture parent directories");
123 }
124
125 fs::write(full_path, content).expect("failed to write fixture file");
126}