use super::*;
use crate::ai::context::ContextParams;
fn empty_params() -> ContextParams<'static> {
ContextParams {
input_schema: None,
base_query: None,
base_query_result: None,
is_empty_result: false,
}
}
#[test]
fn test_poll_without_channel_does_nothing() {
let mut ai_state = AiState::new(true);
poll_response_channel(&mut ai_state);
assert!(!ai_state.loading);
assert!(ai_state.response.is_empty());
}
#[test]
fn test_poll_processes_chunk() {
let mut ai_state = AiState::new(true);
let (_tx, rx) = mpsc::channel();
ai_state.response_rx = Some(rx);
ai_state.loading = true;
let (tx, rx) = mpsc::channel();
ai_state.response_rx = Some(rx);
ai_state.start_request();
let request_id = ai_state.current_request_id();
tx.send(AiResponse::Chunk {
text: "Hello ".to_string(),
request_id,
})
.unwrap();
poll_response_channel(&mut ai_state);
assert_eq!(ai_state.response, "Hello ");
assert!(ai_state.loading); }
#[test]
fn test_poll_processes_multiple_chunks() {
let mut ai_state = AiState::new(true);
let (tx, rx) = mpsc::channel();
ai_state.response_rx = Some(rx);
ai_state.start_request();
let request_id = ai_state.current_request_id();
tx.send(AiResponse::Chunk {
text: "Hello ".to_string(),
request_id,
})
.unwrap();
tx.send(AiResponse::Chunk {
text: "World".to_string(),
request_id,
})
.unwrap();
poll_response_channel(&mut ai_state);
assert_eq!(ai_state.response, "Hello World");
}
#[test]
fn test_poll_processes_complete() {
let mut ai_state = AiState::new(true);
let (tx, rx) = mpsc::channel();
ai_state.response_rx = Some(rx);
ai_state.start_request();
let request_id = ai_state.current_request_id();
ai_state.response = "Full response".to_string();
tx.send(AiResponse::Complete { request_id }).unwrap();
poll_response_channel(&mut ai_state);
assert!(!ai_state.loading);
assert_eq!(ai_state.response, "Full response");
}
#[test]
fn test_poll_processes_error() {
let mut ai_state = AiState::new(true);
let (tx, rx) = mpsc::channel();
ai_state.response_rx = Some(rx);
ai_state.loading = true;
tx.send(AiResponse::Error("Network error".to_string()))
.unwrap();
poll_response_channel(&mut ai_state);
assert!(!ai_state.loading);
assert_eq!(ai_state.error, Some("Network error".to_string()));
}
#[test]
fn test_poll_processes_cancelled() {
let mut ai_state = AiState::new(true);
let (tx, rx) = mpsc::channel();
ai_state.response_rx = Some(rx);
ai_state.start_request();
let request_id = ai_state.current_request_id();
tx.send(AiResponse::Cancelled { request_id }).unwrap();
poll_response_channel(&mut ai_state);
assert!(!ai_state.loading);
assert!(ai_state.in_flight_request_id.is_none());
}
#[test]
fn test_poll_handles_disconnected_channel() {
let mut ai_state = AiState::new(true);
let (tx, rx) = mpsc::channel::<AiResponse>();
ai_state.response_rx = Some(rx);
ai_state.loading = true;
drop(tx);
poll_response_channel(&mut ai_state);
assert!(!ai_state.loading);
assert!(ai_state.error.is_some());
}
#[test]
fn test_poll_empty_channel_does_nothing() {
let mut ai_state = AiState::new(true);
let (_tx, rx) = mpsc::channel::<AiResponse>();
ai_state.response_rx = Some(rx);
ai_state.loading = true;
poll_response_channel(&mut ai_state);
assert!(ai_state.loading);
assert!(ai_state.response.is_empty());
assert!(ai_state.error.is_none());
}
#[test]
fn test_stale_responses_filtered() {
let mut ai_state = AiState::new(true);
let (tx, rx) = mpsc::channel();
ai_state.response_rx = Some(rx);
ai_state.start_request();
let old_request_id = ai_state.current_request_id();
ai_state.start_request();
let new_request_id = ai_state.current_request_id();
assert!(new_request_id > old_request_id);
tx.send(AiResponse::Chunk {
text: "old chunk".to_string(),
request_id: old_request_id,
})
.unwrap();
tx.send(AiResponse::Chunk {
text: "new chunk".to_string(),
request_id: new_request_id,
})
.unwrap();
poll_response_channel(&mut ai_state);
assert_eq!(ai_state.response, "new chunk");
}
#[test]
fn test_stale_complete_filtered() {
let mut ai_state = AiState::new(true);
let (tx, rx) = mpsc::channel();
ai_state.response_rx = Some(rx);
ai_state.start_request();
let old_request_id = ai_state.current_request_id();
ai_state.start_request();
tx.send(AiResponse::Complete {
request_id: old_request_id,
})
.unwrap();
poll_response_channel(&mut ai_state);
assert!(ai_state.loading);
}
#[test]
fn test_query_error_to_success_clears_response() {
let mut ai_state = AiState::new(true);
ai_state.enabled = true;
ai_state.visible = true;
ai_state.response = "Error explanation".to_string();
ai_state.error = Some("Query error".to_string());
ai_state.set_last_query_hash(".invalid");
let result: Result<String, String> = Ok("success output".to_string());
handle_execution_result(&mut ai_state, &result, ".valid", 6, empty_params());
assert!(ai_state.error.is_none());
assert!(ai_state.visible);
}
#[test]
fn test_query_error_to_different_error_clears_response() {
let mut ai_state = AiState::new(true);
ai_state.enabled = true;
ai_state.visible = true;
ai_state.response = "Old error explanation".to_string();
ai_state.set_last_query_hash(".old");
let result: Result<String, String> = Err("new error".to_string());
handle_execution_result(&mut ai_state, &result, ".new", 4, empty_params());
assert!(ai_state.response.is_empty());
}
#[test]
fn test_different_query_same_error_triggers_new_request() {
let mut ai_state = AiState::new(true);
ai_state.enabled = true;
ai_state.response = "Old explanation".to_string();
ai_state.set_last_query_hash(".query1");
let result: Result<String, String> = Err("same error".to_string());
handle_execution_result(&mut ai_state, &result, ".query2", 7, empty_params());
assert!(ai_state.response.is_empty());
}
#[test]
fn test_same_query_same_error_no_change() {
let mut ai_state = AiState::new(true);
ai_state.enabled = true;
ai_state.response = "Existing explanation".to_string();
ai_state.set_last_query_hash(".same");
let result: Result<String, String> = Err("same error".to_string());
handle_execution_result(&mut ai_state, &result, ".same", 5, empty_params());
assert_eq!(ai_state.response, "Existing explanation");
}
#[test]
fn test_same_query_different_error_no_change() {
let mut ai_state = AiState::new(true);
ai_state.enabled = true;
ai_state.response = "Existing explanation".to_string();
ai_state.set_last_query_hash(".same");
let result: Result<String, String> = Err("different error".to_string());
handle_execution_result(&mut ai_state, &result, ".same", 5, empty_params());
assert_eq!(ai_state.response, "Existing explanation");
}
#[test]
fn test_different_query_different_error_triggers_new_request() {
let mut ai_state = AiState::new(true);
ai_state.enabled = true;
ai_state.response = "Old explanation".to_string();
ai_state.set_last_query_hash(".query1");
let result: Result<String, String> = Err("different error".to_string());
handle_execution_result(&mut ai_state, &result, ".query2", 7, empty_params());
assert!(ai_state.response.is_empty());
}
#[test]
fn test_success_triggers_ai_request() {
let mut ai_state = AiState::new(true);
ai_state.enabled = true;
ai_state.visible = true; let (tx, rx) = mpsc::channel();
ai_state.request_tx = Some(tx);
let result: Result<String, String> = Ok("output data".to_string());
handle_execution_result(&mut ai_state, &result, ".name", 5, empty_params());
let request = rx.try_recv();
assert!(request.is_ok(), "Should have sent AI request for success");
let AiRequest::Query { prompt, .. } = request.unwrap();
assert!(
prompt.contains("optimize"),
"Success prompt should mention optimization"
);
}
#[test]
fn test_error_triggers_ai_request() {
let mut ai_state = AiState::new(true);
ai_state.enabled = true;
ai_state.visible = true; let (tx, rx) = mpsc::channel();
ai_state.request_tx = Some(tx);
let result: Result<String, String> = Err("syntax error".to_string());
handle_execution_result(&mut ai_state, &result, ".invalid", 8, empty_params());
let request = rx.try_recv();
assert!(request.is_ok(), "Should have sent AI request for error");
let AiRequest::Query { prompt, .. } = request.unwrap();
assert!(
prompt.contains("troubleshoot"),
"Error prompt should mention troubleshooting"
);
assert!(
prompt.contains("syntax error"),
"Error prompt should contain error message"
);
}
#[test]
fn test_query_change_clears_in_flight_request() {
let mut ai_state = AiState::new(true);
ai_state.enabled = true;
ai_state.visible = true; let (tx, rx) = mpsc::channel();
ai_state.request_tx = Some(tx);
ai_state.start_request();
let _old_request_id = ai_state.current_request_id();
assert!(ai_state.has_in_flight_request());
while rx.try_recv().is_ok() {}
ai_state.set_last_query_hash(".old");
let result: Result<String, String> = Ok("output".to_string());
handle_execution_result(&mut ai_state, &result, ".new", 4, empty_params());
let mut found_query = false;
while let Ok(msg) = rx.try_recv() {
match msg {
AiRequest::Query { .. } => {
found_query = true;
}
}
}
assert!(found_query, "Should have sent new Query request");
}
#[test]
fn test_handle_query_result_wrapper() {
let mut ai_state = AiState::new(true);
ai_state.enabled = true;
ai_state.set_last_query_hash(".old");
let result: Result<&str, String> = Ok("output");
handle_query_result(&mut ai_state, &result, ".new", 4, empty_params());
assert!(!ai_state.is_query_changed(".new"));
}
#[test]
fn test_same_query_no_duplicate_requests() {
let mut ai_state = AiState::new(true);
ai_state.enabled = true;
let (tx, rx) = mpsc::channel();
ai_state.request_tx = Some(tx);
let result: Result<String, String> = Ok(r#""test""#.to_string());
handle_execution_result(&mut ai_state, &result, ".name", 5, empty_params());
while rx.try_recv().is_ok() {}
handle_execution_result(
&mut ai_state,
&result,
".name", 5,
empty_params(),
);
let request = rx.try_recv();
assert!(
request.is_err(),
"Should not send duplicate request for same query"
);
}
#[test]
fn test_ai_disabled_no_requests() {
let mut ai_state = AiState::new(true);
ai_state.enabled = false; let (tx, rx) = mpsc::channel();
ai_state.request_tx = Some(tx);
let result: Result<String, String> = Ok(r#""test""#.to_string());
handle_execution_result(&mut ai_state, &result, ".name", 5, empty_params());
let request = rx.try_recv();
assert!(
request.is_err(),
"Should not send request when AI is disabled"
);
}
#[test]
fn test_visible_sends_requests_on_error() {
let mut ai_state = AiState::new(true);
ai_state.enabled = true;
ai_state.visible = true; let (tx, rx) = mpsc::channel();
ai_state.request_tx = Some(tx);
let result: Result<String, String> = Err("syntax error".to_string());
handle_execution_result(&mut ai_state, &result, ".invalid", 8, empty_params());
let request = rx.try_recv();
assert!(
request.is_ok(),
"Should send AI request when popup is visible"
);
let AiRequest::Query { prompt, .. } = request.unwrap();
assert!(
prompt.contains("troubleshoot"),
"Error prompt should mention troubleshooting"
);
assert!(
prompt.contains("syntax error"),
"Error prompt should contain error message"
);
}
#[test]
fn test_hidden_no_requests_on_error() {
let mut ai_state = AiState::new(true);
ai_state.enabled = true;
ai_state.visible = false; let (tx, rx) = mpsc::channel();
ai_state.request_tx = Some(tx);
let result: Result<String, String> = Err("syntax error".to_string());
handle_execution_result(&mut ai_state, &result, ".invalid", 8, empty_params());
let request = rx.try_recv();
assert!(
request.is_err(),
"Should not send AI request when popup is hidden"
);
}
#[test]
fn test_visible_sends_requests_on_success() {
let mut ai_state = AiState::new(true);
ai_state.enabled = true;
ai_state.visible = true; let (tx, rx) = mpsc::channel();
ai_state.request_tx = Some(tx);
let result: Result<String, String> = Ok(r#""test_value""#.to_string());
handle_execution_result(&mut ai_state, &result, ".name", 5, empty_params());
let request = rx.try_recv();
assert!(
request.is_ok(),
"Should send AI request for success when popup is visible"
);
let AiRequest::Query { prompt, .. } = request.unwrap();
assert!(
prompt.contains("optimize"),
"Success prompt should mention optimization"
);
}
#[test]
fn test_hidden_no_requests_on_success() {
let mut ai_state = AiState::new(true);
ai_state.enabled = true;
ai_state.visible = false; let (tx, rx) = mpsc::channel();
ai_state.request_tx = Some(tx);
let result: Result<String, String> = Ok(r#""test_value""#.to_string());
handle_execution_result(&mut ai_state, &result, ".name", 5, empty_params());
let request = rx.try_recv();
assert!(
request.is_err(),
"Should not send AI request when popup is hidden"
);
}