use super::*;
use std::future::IntoFuture;
use crate::client::CommandExecutor;
async fn spawn_test_daemon() -> (tempfile::TempDir, String) {
use crate::daemon::{api, state::DaemonState};
let root_guard = crate::test_support::hermetic_temp_dir();
let state = std::sync::Arc::new(
DaemonState::with_root_isolated_managed(root_guard.path().to_owned()).await,
);
let router = api::router(std::sync::Arc::clone(&state));
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
let addr = listener.local_addr().unwrap();
tokio::spawn(axum::serve(listener, router).into_future());
(root_guard, format!("http://{addr}"))
}
fn offline_proxy() -> SessionProxy {
SessionProxy::new(Arc::new(CommandExecutor::new("http://127.0.0.1:0")))
}
#[test]
fn route_free_text_focused_injects() {
assert_eq!(route_free_text("build it now", true), FreeTextRoute::Inject);
}
#[test]
fn route_free_text_unfocused_coordinates() {
assert_eq!(
route_free_text("spin up a session", false),
FreeTextRoute::Coordinator
);
}
#[test]
fn route_free_text_slash_never_injects() {
assert_eq!(
route_free_text("/typo arg", true),
FreeTextRoute::Coordinator
);
assert_eq!(route_free_text(" /typo", true), FreeTextRoute::Coordinator);
}
#[test]
fn is_missing_session_detects_not_found() {
assert!(is_missing_session("managed session foo not found"));
assert!(!is_missing_session("send failed: connection refused"));
}
#[tokio::test]
async fn is_missing_session_matches_live_resolver_not_found_format() {
let (_daemon_root, url) = spawn_test_daemon().await;
let backend = CommandExecutor::new(url);
let err = backend
.resolve("no-such-session")
.await
.expect_err("an empty fleet must fail to resolve");
assert!(
is_missing_session(&err),
"resolver's not-found message no longer matches is_missing_session: {err:?}"
);
}
#[test]
fn set_get_clear_round_trip() {
let proxy = offline_proxy();
assert!(proxy.current_focus("c1").is_none());
proxy.lock().insert(
"c1".to_string(),
FocusTarget {
id: "id-1".into(),
name: "api".into(),
},
);
assert_eq!(proxy.current_focus("c1").map(|f| f.id), Some("id-1".into()));
let removed = proxy.unfocus("c1");
assert_eq!(removed.map(|f| f.name), Some("api".to_string()));
assert!(proxy.current_focus("c1").is_none());
assert!(proxy.unfocus("c1").is_none());
}
#[tokio::test]
async fn focus_empty_reports_current() {
let proxy = offline_proxy();
assert_eq!(proxy.focus("c1", " ").await, FocusOutcome::Current(None));
}
#[tokio::test]
async fn focus_unknown_is_not_found() {
let (_daemon_root, url) = spawn_test_daemon().await;
let proxy = SessionProxy::new(Arc::new(CommandExecutor::new(url)));
match proxy.focus("c1", "no-such-session").await {
FocusOutcome::NotFound { target, .. } => assert_eq!(target, "no-such-session"),
other => panic!("expected NotFound, got {other:?}"),
}
assert!(proxy.current_focus("c1").is_none());
}
#[tokio::test]
async fn inject_no_focus() {
let proxy = offline_proxy();
assert_eq!(proxy.inject("c1", "hi").await, InjectOutcome::NoFocus);
}
#[tokio::test]
async fn inject_auto_unfocuses_dead_session() {
let (_daemon_root, url) = spawn_test_daemon().await;
let proxy = SessionProxy::new(Arc::new(CommandExecutor::new(url)));
proxy.lock().insert(
"c1".to_string(),
FocusTarget {
id: "dead-id".into(),
name: "ghost".into(),
},
);
match proxy.inject("c1", "are you there?").await {
InjectOutcome::AutoUnfocused { target, .. } => assert_eq!(target.name, "ghost"),
other => panic!("expected AutoUnfocused, got {other:?}"),
}
assert!(proxy.current_focus("c1").is_none(), "focus auto-cleared");
}
#[tokio::test]
async fn inject_transient_error_keeps_focus() {
let proxy = offline_proxy();
proxy.lock().insert(
"c1".to_string(),
FocusTarget {
id: "id-1".into(),
name: "api".into(),
},
);
match proxy.inject("c1", "hello").await {
InjectOutcome::Failed { target, .. } => assert_eq!(target.name, "api"),
other => panic!("expected Failed, got {other:?}"),
}
assert!(proxy.current_focus("c1").is_some(), "focus preserved");
}
#[tokio::test]
async fn summarize_no_focus() {
let proxy = offline_proxy();
assert_eq!(proxy.summarize("c1").await, SummarizeOutcome::NoFocus);
}
#[tokio::test]
async fn summarize_auto_unfocuses_dead_session() {
let (_daemon_root, url) = spawn_test_daemon().await;
let proxy = SessionProxy::new(Arc::new(CommandExecutor::new(url)));
proxy.lock().insert(
"c1".to_string(),
FocusTarget {
id: "dead-id".into(),
name: "ghost".into(),
},
);
match proxy.summarize("c1").await {
SummarizeOutcome::AutoUnfocused { target, .. } => assert_eq!(target.name, "ghost"),
other => panic!("expected AutoUnfocused, got {other:?}"),
}
assert!(proxy.current_focus("c1").is_none(), "focus auto-cleared");
}