feat(tickets): création d'un assistant IA de ticket (backend + frontend)
Ajoute le chat assistant IA attaché à un ticket (#8). Backend Rust : - use cases OpenTicketAssistant/CloseTicketAssistant + tests - politique d'outils par agent (domain/agent_tool_policy) et policy MCP - store de contexte assistant + gabarit default_ticket_assistant.md + tests - events TicketAssistantOpened/Closed - commandes Tauri open_ticket_chat/close_ticket_chat et câblage state/lib/events Frontend : - gateway (ports, adapters ticket + mock, domain) - hook useTicketAssistant + composant TicketAssistantPanel - intégration dans TicketDetail Tests verts : vitest tickets.test.tsx (23), cargo test application::ticket_assistant (1), infrastructure::assistant_context_store (2). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
110
crates/infrastructure/tests/assistant_context_store.rs
Normal file
110
crates/infrastructure/tests/assistant_context_store.rs
Normal file
@ -0,0 +1,110 @@
|
||||
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<dyn FileSystem> = 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"));
|
||||
}
|
||||
Reference in New Issue
Block a user