1use super::{Message, Role, ToolDefinition};
2
3impl super::PawanAgent {
4 pub async fn preflight_check(&self) -> crate::Result<()> {
7 let test = vec![Message {
8 role: Role::User,
9 content: "ping".into(),
10 tool_calls: vec![],
11 tool_result: None,
12 }];
13 let tools: Vec<ToolDefinition> = vec![];
14 match self.backend.generate(&test, &tools, None).await {
15 Ok(_) => Ok(()),
16 Err(e) => Err(crate::PawanError::Llm(format!("Model unreachable: {}", e))),
17 }
18 }
19}
20
21#[cfg(test)]
22mod tests {
23 use super::*;
24 use crate::agent::backend::mock::MockBackend;
25 use crate::agent::backend::LlmBackend;
26 use crate::agent::{LLMResponse, PawanAgent, TokenCallback};
27 use crate::config::PawanConfig;
28 use async_trait::async_trait;
29 use std::path::PathBuf;
30
31 struct AlwaysFailBackend;
32
33 #[async_trait]
34 impl LlmBackend for AlwaysFailBackend {
35 async fn generate(
36 &self,
37 _messages: &[Message],
38 _tools: &[ToolDefinition],
39 _on_token: Option<&TokenCallback>,
40 ) -> crate::Result<LLMResponse> {
41 Err(crate::PawanError::Llm("connection refused".into()))
42 }
43 }
44
45 fn agent_with_backend(backend: Box<dyn LlmBackend>) -> PawanAgent {
46 PawanAgent::new(PawanConfig::default(), PathBuf::from("."))
47 .with_backend(backend)
48 }
49
50 #[tokio::test]
51 async fn preflight_check_ok_when_backend_responds() {
52 let agent = agent_with_backend(Box::new(MockBackend::with_text("pong")));
53 assert!(agent.preflight_check().await.is_ok());
54 }
55
56 #[tokio::test]
57 async fn preflight_check_ok_when_backend_returns_empty_text() {
58 let agent = agent_with_backend(Box::new(MockBackend::with_text("")));
59 assert!(agent.preflight_check().await.is_ok());
60 }
61
62 #[tokio::test]
63 async fn preflight_check_errors_when_backend_fails() {
64 let agent = agent_with_backend(Box::new(AlwaysFailBackend));
65 let err = agent
66 .preflight_check()
67 .await
68 .expect_err("failing backend must bubble out");
69 assert!(
70 matches!(err, crate::PawanError::Llm(_)),
71 "expected Llm error variant, got {err:?}"
72 );
73 }
74
75 #[tokio::test]
76 async fn preflight_check_error_message_mentions_unreachable() {
77 let agent = agent_with_backend(Box::new(AlwaysFailBackend));
78 let err = agent.preflight_check().await.unwrap_err();
79 let msg = err.to_string();
80 assert!(
81 msg.contains("Model unreachable"),
82 "error should be wrapped with 'Model unreachable', got: {msg}"
83 );
84 }
85
86 #[tokio::test]
87 async fn preflight_check_does_not_mutate_agent_history() {
88 let agent = agent_with_backend(Box::new(MockBackend::with_text("pong")));
89 let before = agent.history().len();
90 agent.preflight_check().await.unwrap();
91 assert_eq!(
92 agent.history().len(),
93 before,
94 "preflight must not persist the ping message into agent history"
95 );
96 }
97}