- LOT C2 (§14.5.3) : use cases de configuration des embedders déclaratifs (List/Save/Delete + DescribeEmbedderEngines : modèles ONNX recommandés, environnement local détecté, stratégies compilées). UI EmbedderSettings. - LOT C3 (§14.5.5) : suggestion contextuelle best-effort à l'activation quand la mémoire dépasse le budget de recall sans embedder configuré (event EmbedderSuggested, anti-spam 1×/session, « ne plus demander »). - Contexte projet partagé .ideai/CONTEXT.md (model-agnostic) injecté à tous les agents/profils au lancement, avant la persona. UI ProjectContextPanel. Tests : backend workspace vert (0 échec) ; frontend 306/306. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
104 lines
3.4 KiB
Rust
104 lines
3.4 KiB
Rust
//! Integration test for the orchestrator wiring in the composition root
|
|
//! (ARCHITECTURE §14.3).
|
|
//!
|
|
//! These tests prove that [`AppState`] actually *starts and stops* per-project
|
|
//! orchestrator watchers — the gap that previously left the whole §14.3 feature
|
|
//! dormant (the `OrchestratorService`/watcher existed but were never constructed
|
|
//! at runtime). The per-file request→dispatch→response behaviour is covered by
|
|
//! the infrastructure watcher tests; here we assert the lifecycle the open/close
|
|
//! commands rely on: registration is idempotent, projects are isolated, and
|
|
//! stopping unregisters.
|
|
|
|
use std::path::PathBuf;
|
|
|
|
use app_tauri_lib::state::AppState;
|
|
use domain::ports::IdGenerator;
|
|
use domain::project::{Project, ProjectPath};
|
|
use domain::remote::RemoteRef;
|
|
use domain::ProjectId;
|
|
use infrastructure::UuidGenerator;
|
|
|
|
/// A unique, absolute temp path (never written to at build time — the stores are
|
|
/// lazy — so it need not exist).
|
|
fn temp_path(tag: &str) -> PathBuf {
|
|
let ids = UuidGenerator::new();
|
|
std::env::temp_dir().join(format!("idea-orch-test-{tag}-{}", ids.new_uuid()))
|
|
}
|
|
|
|
/// Builds a domain [`Project`] rooted at a fresh temp path.
|
|
fn make_project() -> Project {
|
|
let ids = UuidGenerator::new();
|
|
let root = temp_path("root");
|
|
Project::new(
|
|
ProjectId::from_uuid(ids.new_uuid()),
|
|
"demo",
|
|
ProjectPath::new(root.to_string_lossy().into_owned()).unwrap(),
|
|
RemoteRef::local(),
|
|
1_700_000_000_000,
|
|
)
|
|
.unwrap()
|
|
}
|
|
|
|
fn watcher_count(state: &AppState) -> usize {
|
|
state.orchestrator_watchers.lock().unwrap().len()
|
|
}
|
|
|
|
fn has_watcher(state: &AppState, id: &ProjectId) -> bool {
|
|
state.orchestrator_watchers.lock().unwrap().contains_key(id)
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn ensure_watch_registers_a_watcher_and_is_idempotent() {
|
|
let state = AppState::build(temp_path("appdata"));
|
|
let project = make_project();
|
|
|
|
assert_eq!(watcher_count(&state), 0, "no watcher before open");
|
|
|
|
state.ensure_orchestrator_watch(&project);
|
|
assert!(has_watcher(&state, &project.id));
|
|
assert_eq!(watcher_count(&state), 1);
|
|
|
|
// Opening the same project again must not spawn a second watcher.
|
|
state.ensure_orchestrator_watch(&project);
|
|
assert_eq!(watcher_count(&state), 1, "ensure is idempotent per project");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn stop_watch_unregisters_the_watcher() {
|
|
let state = AppState::build(temp_path("appdata"));
|
|
let project = make_project();
|
|
|
|
state.ensure_orchestrator_watch(&project);
|
|
assert!(has_watcher(&state, &project.id));
|
|
|
|
state.stop_orchestrator_watch(&project.id);
|
|
assert!(
|
|
!has_watcher(&state, &project.id),
|
|
"watcher removed on close"
|
|
);
|
|
assert_eq!(watcher_count(&state), 0);
|
|
|
|
// Stopping an unknown project is a no-op (does not panic).
|
|
state.stop_orchestrator_watch(&project.id);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn watchers_are_isolated_per_project() {
|
|
let state = AppState::build(temp_path("appdata"));
|
|
let a = make_project();
|
|
let b = make_project();
|
|
|
|
state.ensure_orchestrator_watch(&a);
|
|
state.ensure_orchestrator_watch(&b);
|
|
|
|
assert_eq!(watcher_count(&state), 2);
|
|
assert!(has_watcher(&state, &a.id));
|
|
assert!(has_watcher(&state, &b.id));
|
|
|
|
// Closing one leaves the other running.
|
|
state.stop_orchestrator_watch(&a.id);
|
|
assert!(!has_watcher(&state, &a.id));
|
|
assert!(has_watcher(&state, &b.id));
|
|
assert_eq!(watcher_count(&state), 1);
|
|
}
|