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(".")).with_backend(backend)
47 }
48
49 #[tokio::test]
50 async fn preflight_check_ok_when_backend_responds() {
51 let agent = agent_with_backend(Box::new(MockBackend::with_text("pong")));
52 assert!(agent.preflight_check().await.is_ok());
53 }
54
55 #[tokio::test]
56 async fn preflight_check_ok_when_backend_returns_empty_text() {
57 let agent = agent_with_backend(Box::new(MockBackend::with_text("")));
58 assert!(agent.preflight_check().await.is_ok());
59 }
60
61 #[tokio::test]
62 async fn preflight_check_errors_when_backend_fails() {
63 let agent = agent_with_backend(Box::new(AlwaysFailBackend));
64 let err = agent
65 .preflight_check()
66 .await
67 .expect_err("failing backend must bubble out");
68 assert!(
69 matches!(err, crate::PawanError::Llm(_)),
70 "expected Llm error variant, got {err:?}"
71 );
72 }
73
74 #[tokio::test]
75 async fn preflight_check_error_message_mentions_unreachable() {
76 let agent = agent_with_backend(Box::new(AlwaysFailBackend));
77 let err = agent.preflight_check().await.unwrap_err();
78 let msg = err.to_string();
79 assert!(
80 msg.contains("Model unreachable"),
81 "error should be wrapped with 'Model unreachable', got: {msg}"
82 );
83 }
84
85 #[tokio::test]
86 async fn preflight_check_does_not_mutate_agent_history() {
87 let agent = agent_with_backend(Box::new(MockBackend::with_text("pong")));
88 let before = agent.history().len();
89 agent.preflight_check().await.unwrap();
90 assert_eq!(
91 agent.history().len(),
92 before,
93 "preflight must not persist the ping message into agent history"
94 );
95 }
96}