use std::path::PathBuf; use std::sync::Arc; use domain::{ AssistantContextProvider, FileSystem, Issue, IssueActor, IssueId, IssueNumber, IssuePriority, IssueStatus, MarkdownDoc, Project, ProjectId, ProjectPath, }; use infrastructure::{FsAssistantContextStore, LocalFileSystem}; use uuid::Uuid; struct TempDir(PathBuf); impl TempDir { fn new() -> Self { let path = std::env::temp_dir().join(format!("idea-assistant-context-{}", Uuid::new_v4())); std::fs::create_dir_all(&path).unwrap(); Self(path) } fn app_data_dir(&self) -> String { self.0.join("app-data").to_string_lossy().into_owned() } fn project_root(&self) -> ProjectPath { ProjectPath::new(self.0.join("project").to_string_lossy().into_owned()).unwrap() } } impl Drop for TempDir { fn drop(&mut self) { let _ = std::fs::remove_dir_all(&self.0); } } fn project(root: ProjectPath) -> Project { Project::new( ProjectId::from_uuid(Uuid::from_u128(1)), "demo", root, domain::remote::RemoteRef::local(), 1_000, ) .unwrap() } fn issue(number: u64) -> Issue { Issue::new( IssueId::from_uuid(Uuid::from_u128(number as u128)), IssueNumber::new(number).unwrap(), "Clarify onboarding", MarkdownDoc::new("Initial description body"), IssueStatus::Open, IssuePriority::High, MarkdownDoc::new("Carnet notes"), Vec::new(), Vec::new(), IssueActor::User, 1_000, ) .unwrap() } fn store(app_data_dir: String) -> FsAssistantContextStore { let fs: Arc = Arc::new(LocalFileSystem::new()); FsAssistantContextStore::new(fs, app_data_dir) } #[tokio::test] async fn default_context_is_embedded_and_injects_the_ticket() { let tmp = TempDir::new(); let project = project(tmp.project_root()); let store = store(tmp.app_data_dir()); let ctx = store .prepare_ticket_assistant_context(&project, &issue(7)) .await .unwrap(); let body = ctx.content.as_str(); assert!(body.contains("# Ticket Assistant")); assert!(body.contains("- Ref: #7")); assert!(body.contains("- Title: Clarify onboarding")); assert!(body.contains("Initial description body")); assert!(body.contains("Carnet notes")); assert_eq!(ctx.project_root, project.root.as_str()); assert_eq!(ctx.relative_path, "ticket-assistant.md"); } #[tokio::test] async fn app_data_override_replaces_the_embedded_default() { let tmp = TempDir::new(); let app_data_dir = tmp.app_data_dir(); let override_path = PathBuf::from(&app_data_dir) .join("assistant") .join("ticket-assistant.md"); std::fs::create_dir_all(override_path.parent().unwrap()).unwrap(); std::fs::write(&override_path, "# Custom assistant").unwrap(); let project = project(tmp.project_root()); let store = store(app_data_dir); let ctx = store .prepare_ticket_assistant_context(&project, &issue(8)) .await .unwrap(); let body = ctx.content.as_str(); assert!(body.starts_with("# Custom assistant")); assert!(!body.contains("You are IdeA's ticket editing assistant")); assert!(body.contains("- Ref: #8")); }