use super::*;
use crate::autocomplete::json_navigator::DEFAULT_ARRAY_SAMPLE_SIZE;
use crate::query::worker::preprocess::{parse_and_detect_type, strip_ansi_codes};
#[path = "query_state_tests/async_preprocessing_tests.rs"]
mod async_preprocessing_tests;
#[test]
fn test_new_query_state() {
let json = r#"{"name": "test"}"#;
let state = QueryState::new(json.to_string());
assert!(state.result.is_ok());
assert!(state.last_successful_result.is_some());
}
#[test]
fn test_execute_updates_result() {
let json = r#"{"name": "test", "age": 30}"#;
let mut state = QueryState::new(json.to_string());
state.execute(".name");
assert!(state.result.is_ok());
assert!(state.last_successful_result.is_some());
}
#[test]
fn test_execute_caches_successful_result() {
let json = r#"{"value": 42}"#;
let mut state = QueryState::new(json.to_string());
state.execute(".value");
let cached = state.last_successful_result.clone();
assert!(cached.is_some());
state.execute(".[invalid syntax");
assert!(state.result.is_err());
assert_eq!(state.last_successful_result, cached);
}
#[test]
fn test_line_count_with_ok_result() {
let json = r#"{"test": true}"#;
let mut state = QueryState::new(json.to_string());
let content: String = (0..50).map(|i| format!("line{}\n", i)).collect();
state.result = Ok(content.clone());
state.last_successful_result = Some(Arc::new(content.clone()));
state.cached_line_count = content.lines().count() as u32;
assert_eq!(state.line_count(), 50);
}
#[test]
fn test_line_count_uses_cached_on_error() {
let json = r#"{"test": true}"#;
let mut state = QueryState::new(json.to_string());
let valid_result: String = (0..30).map(|i| format!("line{}\n", i)).collect();
state.result = Ok(valid_result.clone());
state.last_successful_result = Some(Arc::new(valid_result.clone()));
state.cached_line_count = valid_result.lines().count() as u32;
state.result = Err("syntax error".to_string());
assert_eq!(state.line_count(), 30);
}
#[test]
fn test_line_count_zero_on_error_without_cache() {
let json = r#"{"test": true}"#;
let mut state = QueryState::new(json.to_string());
state.result = Err("error".to_string());
state.last_successful_result = None;
state.cached_line_count = 0;
assert_eq!(state.line_count(), 0);
}
#[test]
fn test_null_results_dont_overwrite_cache() {
let json = r#"{"name": "test", "age": 30}"#;
let mut state = QueryState::new(json.to_string());
let initial_cache = state.last_successful_result.clone();
let initial_unformatted = state.last_successful_result_unformatted.clone();
assert!(initial_cache.is_some());
assert!(initial_unformatted.is_some());
state.execute(".nonexistent");
assert!(state.result.is_ok());
assert!(state.result.as_ref().unwrap().contains("null"));
assert_eq!(state.last_successful_result, initial_cache);
assert_eq!(
state.last_successful_result_unformatted,
initial_unformatted
);
state.execute(".name");
assert!(state.result.as_ref().unwrap().contains("test"));
assert_ne!(state.last_successful_result, initial_cache);
assert_ne!(
state.last_successful_result_unformatted,
initial_unformatted
);
assert!(
state
.last_successful_result
.as_ref()
.unwrap()
.contains("test")
);
let unformatted = state.last_successful_result_unformatted.as_ref().unwrap();
assert!(!unformatted.contains("\x1b"));
assert!(unformatted.contains("test"));
}
#[test]
fn test_strip_ansi_codes_simple() {
let input = "\x1b[0m{\x1b[1;39m\"name\"\x1b[0m: \x1b[0;32m\"test\"\x1b[0m}";
let output = strip_ansi_codes(input);
assert_eq!(output, r#"{"name": "test"}"#);
assert!(!output.contains("\x1b"));
}
#[test]
fn test_strip_ansi_codes_complex() {
let input = "\x1b[1;39m{\x1b[0m\n \x1b[0;34m\"key\"\x1b[0m: \x1b[0;32m\"value\"\x1b[0m\n\x1b[1;39m}\x1b[0m";
let output = strip_ansi_codes(input);
assert!(output.contains(r#""key""#));
assert!(output.contains(r#""value""#));
assert!(!output.contains("\x1b"));
}
#[test]
fn test_strip_ansi_codes_no_codes() {
let input = r#"{"name": "plain"}"#;
let output = strip_ansi_codes(input);
assert_eq!(output, input);
}
#[test]
fn test_strip_ansi_codes_null_with_color() {
let input = "\x1b[0;90mnull\x1b[0m";
let output = strip_ansi_codes(input);
assert_eq!(output, "null");
}
#[test]
fn test_unformatted_result_stored_on_execute() {
let json = r#"{"name": "test"}"#;
let mut state = QueryState::new(json.to_string());
state.execute(".name");
assert!(state.last_successful_result.is_some());
assert!(state.last_successful_result_unformatted.is_some());
let unformatted = state.last_successful_result_unformatted.as_ref().unwrap();
assert!(!unformatted.contains("\x1b"));
}
#[test]
fn test_unformatted_result_handles_multiline_objects() {
let json = r#"{"items": [{"id": 1, "name": "a"}, {"id": 2, "name": "b"}]}"#;
let mut state = QueryState::new(json.to_string());
state.execute(".items[]");
let unformatted = state.last_successful_result_unformatted.as_ref().unwrap();
assert!(!unformatted.contains("\x1b"));
assert!(unformatted.contains("id"));
assert!(unformatted.contains("name"));
}
#[test]
fn test_multiple_nulls_dont_overwrite_cache() {
let json = r#"{"items": [{"id": 1}, {"id": 2}, {"id": 3}]}"#;
let mut state = QueryState::new(json.to_string());
state.execute(".items[]");
let cached_after_items = state.last_successful_result_unformatted.clone();
assert!(cached_after_items.is_some());
assert!(cached_after_items.as_ref().unwrap().contains("id"));
state.execute(".items[].nonexistent");
assert!(state.result.as_ref().unwrap().contains("null"));
assert_eq!(state.last_successful_result_unformatted, cached_after_items);
}
#[test]
fn test_parsed_result_cached_for_autocomplete() {
let json =
r#"{"services": [{"name": "svc1", "arn": "arn1"}, {"name": "svc2", "arn": "arn2"}]}"#;
let mut state = QueryState::new(json.to_string());
assert!(state.last_successful_result_parsed.is_some());
state.execute(".services");
assert!(state.last_successful_result_parsed.is_some());
state.execute(".services[]");
assert!(state.last_successful_result_parsed.is_some());
let parsed = state.last_successful_result_parsed.as_ref().unwrap();
assert!(parsed.is_object());
assert!(parsed.get("name").is_some());
assert!(parsed.get("arn").is_some());
}
#[test]
fn test_parsed_result_handles_destructured_output() {
let json = r#"{"items": [{"id": 1, "value": "a"}, {"id": 2, "value": "b"}]}"#;
let mut state = QueryState::new(json.to_string());
state.execute(".items[]");
let result = state.result.as_ref().unwrap();
let line_count = result.lines().count();
assert!(
line_count >= 2,
"Should have multiple lines for destructured output"
);
assert!(state.last_successful_result_parsed.is_some());
let parsed = state.last_successful_result_parsed.as_ref().unwrap();
assert!(parsed.is_object());
assert_eq!(parsed.get("id").and_then(|v| v.as_i64()), Some(1));
assert_eq!(parsed.get("value").and_then(|v| v.as_str()), Some("a"));
}
#[test]
fn test_parse_first_value_handles_single_object() {
let json = r#"{"name": "test", "value": 42}"#;
let (parsed, result_type) = parse_and_detect_type(json, DEFAULT_ARRAY_SAMPLE_SIZE);
assert!(parsed.is_some());
let value = parsed.unwrap();
assert!(value.is_object());
assert_eq!(value.get("name").and_then(|v| v.as_str()), Some("test"));
assert_eq!(result_type, ResultType::Object);
}
#[test]
fn test_parse_first_value_handles_destructured_objects() {
let json = "{\"name\": \"first\"}\n{\"name\": \"second\"}\n{\"name\": \"third\"}";
let (parsed, result_type) = parse_and_detect_type(json, DEFAULT_ARRAY_SAMPLE_SIZE);
assert!(parsed.is_some());
let value = parsed.unwrap();
assert!(value.is_object());
assert_eq!(value.get("name").and_then(|v| v.as_str()), Some("first"));
assert_eq!(result_type, ResultType::DestructuredObjects);
}
#[test]
fn test_parse_first_value_handles_array() {
let json = r#"[{"id": 1}, {"id": 2}]"#;
let (parsed, result_type) = parse_and_detect_type(json, DEFAULT_ARRAY_SAMPLE_SIZE);
assert!(parsed.is_some());
let value = parsed.unwrap();
assert!(value.is_array());
assert_eq!(result_type, ResultType::ArrayOfObjects);
}
#[test]
fn test_parse_first_value_returns_none_for_empty() {
assert!(
parse_and_detect_type("", DEFAULT_ARRAY_SAMPLE_SIZE)
.0
.is_none()
);
assert!(
parse_and_detect_type(" ", DEFAULT_ARRAY_SAMPLE_SIZE)
.0
.is_none()
);
}
#[test]
fn test_parse_first_value_returns_none_for_invalid_json() {
assert!(
parse_and_detect_type("invalid json {", DEFAULT_ARRAY_SAMPLE_SIZE)
.0
.is_none()
);
assert!(
parse_and_detect_type("not json at all", DEFAULT_ARRAY_SAMPLE_SIZE)
.0
.is_none()
);
}
#[test]
fn test_execute_async_basic_flow() {
let json = r#"{"name": "test", "value": 42}"#;
let mut state = QueryState::new(json.to_string());
state.execute_async(".name");
assert!(state.is_pending());
assert!(state.in_flight_request_id.is_some());
let timeout = std::time::Instant::now();
while state.is_pending() && timeout.elapsed() < std::time::Duration::from_secs(2) {
let _ = state.poll_response();
std::thread::sleep(std::time::Duration::from_millis(10));
}
assert!(!state.is_pending());
assert!(state.result.is_ok());
assert!(state.result.as_ref().unwrap().contains("test"));
}
#[test]
fn test_execute_async_cancellation() {
let json = r#"{"data": "value"}"#;
let mut state = QueryState::new(json.to_string());
state.execute_async(".data");
let first_request_id = state.in_flight_request_id;
assert!(first_request_id.is_some());
state.execute_async(".data | length");
let second_request_id = state.in_flight_request_id;
assert!(second_request_id.is_some());
assert_ne!(first_request_id, second_request_id);
let timeout = std::time::Instant::now();
while state.is_pending() && timeout.elapsed() < std::time::Duration::from_secs(2) {
let _ = state.poll_response();
std::thread::sleep(std::time::Duration::from_millis(10));
}
assert!(!state.is_pending());
assert!(state.result.is_ok());
}
#[test]
fn test_poll_response_filters_stale_responses() {
let json = r#"{"test": true}"#;
let mut state = QueryState::new(json.to_string());
state.execute_async(".");
let first_id = state.in_flight_request_id.unwrap();
state.execute_async(".test");
let second_id = state.in_flight_request_id.unwrap();
assert_ne!(first_id, second_id);
let timeout = std::time::Instant::now();
while state.is_pending() && timeout.elapsed() < std::time::Duration::from_secs(2) {
let _ = state.poll_response();
std::thread::sleep(std::time::Duration::from_millis(10));
}
assert!(!state.is_pending());
}
#[test]
fn test_request_id_increments_correctly() {
let json = r#"{"test": true}"#;
let mut state = QueryState::new(json.to_string());
state.execute_async(".");
assert_eq!(state.in_flight_request_id, Some(1));
state.cancel_in_flight();
state.execute_async(".test");
assert_eq!(state.in_flight_request_id, Some(2));
state.cancel_in_flight();
state.execute_async(".");
assert_eq!(state.in_flight_request_id, Some(3));
}
#[test]
fn test_is_pending_tracks_state_correctly() {
let json = r#"{"test": true}"#;
let mut state = QueryState::new(json.to_string());
assert!(!state.is_pending());
state.execute_async(".");
assert!(state.is_pending());
let timeout = std::time::Instant::now();
while state.is_pending() && timeout.elapsed() < std::time::Duration::from_secs(2) {
let _ = state.poll_response();
std::thread::sleep(std::time::Duration::from_millis(10));
}
assert!(!state.is_pending());
}
#[test]
fn test_cancel_in_flight_clears_state() {
let json = r#"{"test": true}"#;
let mut state = QueryState::new(json.to_string());
state.execute_async(".");
assert!(state.is_pending());
assert!(state.in_flight_request_id.is_some());
assert!(state.current_cancel_token.is_some());
state.cancel_in_flight();
assert!(!state.is_pending());
assert!(state.in_flight_request_id.is_none());
assert!(state.current_cancel_token.is_none());
}
#[test]
fn test_async_execution_updates_base_query_for_suggestions() {
let json = r#"{"services": [{"name": "svc1"}, {"name": "svc2"}]}"#;
let mut state = QueryState::new(json.to_string());
state.execute_async(".services");
let timeout = std::time::Instant::now();
while state.is_pending() && timeout.elapsed() < std::time::Duration::from_secs(2) {
let _ = state.poll_response();
std::thread::sleep(std::time::Duration::from_millis(10));
}
assert_eq!(
state.base_query_for_suggestions,
Some(".services".to_string()),
"Async execution should update base_query_for_suggestions"
);
assert!(
state.last_successful_result_parsed.is_some(),
"Async execution should cache parsed result"
);
}
#[test]
fn test_async_execution_handles_errors_correctly() {
let json = r#"{"test": true}"#;
let mut state = QueryState::new(json.to_string());
state.execute_async(".invalid syntax [");
let timeout = std::time::Instant::now();
while state.is_pending() && timeout.elapsed() < std::time::Duration::from_secs(2) {
let _ = state.poll_response();
std::thread::sleep(std::time::Duration::from_millis(10));
}
assert!(state.result.is_err());
assert!(!state.is_pending());
}
#[test]
fn test_poll_response_returns_completed_query() {
let json = r#"{"name": "test", "value": 42}"#;
let mut state = QueryState::new(json.to_string());
state.execute_async(".name");
let timeout = std::time::Instant::now();
let mut completed_query = None;
while timeout.elapsed() < std::time::Duration::from_secs(2) {
if let Some(query) = state.poll_response() {
completed_query = Some(query);
break;
}
std::thread::sleep(std::time::Duration::from_millis(10));
}
assert_eq!(
completed_query,
Some(".name".to_string()),
"poll_response should return the query that produced the result"
);
}
#[test]
fn test_poll_response_returns_query_for_errors() {
let json = r#"{"test": true}"#;
let mut state = QueryState::new(json.to_string());
let error_query = ".invalid syntax [";
state.execute_async(error_query);
let timeout = std::time::Instant::now();
let mut completed_query = None;
while timeout.elapsed() < std::time::Duration::from_secs(2) {
if let Some(query) = state.poll_response() {
completed_query = Some(query);
break;
}
std::thread::sleep(std::time::Duration::from_millis(10));
}
assert_eq!(
completed_query,
Some(error_query.to_string()),
"poll_response should return query even for errors (for AI context)"
);
assert!(state.result.is_err());
}
#[test]
fn test_detect_array_of_objects() {
let result = r#"[{"id": 1, "name": "a"}, {"id": 2, "name": "b"}]"#;
assert_eq!(
parse_and_detect_type(result, DEFAULT_ARRAY_SAMPLE_SIZE).1,
ResultType::ArrayOfObjects
);
}
#[test]
fn test_detect_empty_array() {
let result = "[]";
assert_eq!(
parse_and_detect_type(result, DEFAULT_ARRAY_SAMPLE_SIZE).1,
ResultType::Array
);
}
#[test]
fn test_detect_array_of_primitives() {
let result = "[1, 2, 3, 4, 5]";
assert_eq!(
parse_and_detect_type(result, DEFAULT_ARRAY_SAMPLE_SIZE).1,
ResultType::Array
);
let result = r#"["a", "b", "c"]"#;
assert_eq!(
parse_and_detect_type(result, DEFAULT_ARRAY_SAMPLE_SIZE).1,
ResultType::Array
);
let result = "[true, false, true]";
assert_eq!(
parse_and_detect_type(result, DEFAULT_ARRAY_SAMPLE_SIZE).1,
ResultType::Array
);
}
#[test]
fn test_detect_destructured_objects() {
let result = r#"{"id": 1, "name": "a"}
{"id": 2, "name": "b"}
{"id": 3, "name": "c"}"#;
assert_eq!(
parse_and_detect_type(result, DEFAULT_ARRAY_SAMPLE_SIZE).1,
ResultType::DestructuredObjects
);
}
#[test]
fn test_detect_destructured_objects_pretty_printed() {
let result = r#"{
"id": 1,
"name": "a"
}
{
"id": 2,
"name": "b"
}"#;
assert_eq!(
parse_and_detect_type(result, DEFAULT_ARRAY_SAMPLE_SIZE).1,
ResultType::DestructuredObjects
);
}
#[test]
fn test_detect_single_object() {
let result = r#"{"name": "test", "age": 30}"#;
assert_eq!(
parse_and_detect_type(result, DEFAULT_ARRAY_SAMPLE_SIZE).1,
ResultType::Object
);
}
#[test]
fn test_detect_single_object_pretty_printed() {
let result = r#"{
"name": "test",
"age": 30
}"#;
assert_eq!(
parse_and_detect_type(result, DEFAULT_ARRAY_SAMPLE_SIZE).1,
ResultType::Object
);
}
#[test]
fn test_detect_string() {
let result = r#""hello world""#;
assert_eq!(
parse_and_detect_type(result, DEFAULT_ARRAY_SAMPLE_SIZE).1,
ResultType::String
);
}
#[test]
fn test_detect_number() {
let result = "42";
assert_eq!(
parse_and_detect_type(result, DEFAULT_ARRAY_SAMPLE_SIZE).1,
ResultType::Number
);
let result = "3.14159";
assert_eq!(
parse_and_detect_type(result, DEFAULT_ARRAY_SAMPLE_SIZE).1,
ResultType::Number
);
let result = "-100";
assert_eq!(
parse_and_detect_type(result, DEFAULT_ARRAY_SAMPLE_SIZE).1,
ResultType::Number
);
}
#[test]
fn test_detect_boolean() {
let result = "true";
assert_eq!(
parse_and_detect_type(result, DEFAULT_ARRAY_SAMPLE_SIZE).1,
ResultType::Boolean
);
let result = "false";
assert_eq!(
parse_and_detect_type(result, DEFAULT_ARRAY_SAMPLE_SIZE).1,
ResultType::Boolean
);
}
#[test]
fn test_detect_null() {
let result = "null";
assert_eq!(
parse_and_detect_type(result, DEFAULT_ARRAY_SAMPLE_SIZE).1,
ResultType::Null
);
}
#[test]
fn test_detect_invalid_json_returns_null() {
let result = "not valid json";
assert_eq!(
parse_and_detect_type(result, DEFAULT_ARRAY_SAMPLE_SIZE).1,
ResultType::Null
);
}
#[test]
fn test_detect_multiple_primitives() {
let result = r#""value1"
"value2"
"value3""#;
assert_eq!(
parse_and_detect_type(result, DEFAULT_ARRAY_SAMPLE_SIZE).1,
ResultType::String
);
}
#[test]
fn test_normalize_strips_pipe_with_identity() {
assert_eq!(
QueryState::normalize_base_query(".services | ."),
".services"
);
assert_eq!(QueryState::normalize_base_query(".items[] | ."), ".items[]");
}
#[test]
fn test_normalize_strips_incomplete_pipe() {
assert_eq!(QueryState::normalize_base_query(".services |"), ".services");
assert_eq!(QueryState::normalize_base_query(".items[] | "), ".items[]");
}
#[test]
fn test_normalize_strips_trailing_dot() {
assert_eq!(QueryState::normalize_base_query(".services."), ".services");
assert_eq!(
QueryState::normalize_base_query(".services[]."),
".services[]"
);
assert_eq!(
QueryState::normalize_base_query(".user.profile."),
".user.profile"
);
}
#[test]
fn test_normalize_strips_trailing_whitespace() {
assert_eq!(QueryState::normalize_base_query(".services "), ".services");
assert_eq!(QueryState::normalize_base_query(".services "), ".services");
assert_eq!(QueryState::normalize_base_query(".services\t"), ".services");
}
#[test]
fn test_normalize_preserves_root() {
assert_eq!(QueryState::normalize_base_query("."), ".");
}
#[test]
fn test_normalize_preserves_complete_queries() {
assert_eq!(QueryState::normalize_base_query(".services"), ".services");
assert_eq!(
QueryState::normalize_base_query(".services[]"),
".services[]"
);
assert_eq!(QueryState::normalize_base_query(".user.name"), ".user.name");
}
#[test]
fn test_normalize_handles_complex_patterns() {
assert_eq!(QueryState::normalize_base_query(".a | .b | ."), ".a | .b");
assert_eq!(
QueryState::normalize_base_query(".services[].config | ."),
".services[].config"
);
}
#[test]
fn test_new_populates_context_cache() {
let json = r#"{"name": "test"}"#;
let state = QueryState::new(json.to_string());
assert!(
state.last_successful_result_for_context.is_some(),
"new() should populate AI context cache"
);
}
#[test]
fn test_successful_query_updates_context_cache() {
let json = r#"{"name": "test", "value": 42}"#;
let mut state = QueryState::new(json.to_string());
state.execute(".name");
assert!(state.result.is_ok());
assert!(
state.last_successful_result_for_context.is_some(),
"Context cache should be populated after successful query"
);
}
#[test]
fn test_null_result_preserves_context_cache() {
let json = r#"{"name": "test", "age": 30}"#;
let mut state = QueryState::new(json.to_string());
let initial_context_cache = state.last_successful_result_for_context.clone();
assert!(initial_context_cache.is_some());
state.execute(".nonexistent");
assert!(state.result.is_ok());
assert!(state.result.as_ref().unwrap().contains("null"));
assert_eq!(
state.last_successful_result_for_context, initial_context_cache,
"Context cache should be preserved for null-only results"
);
}
#[test]
fn test_context_cache_contains_processed_data() {
use crate::ai::context::MAX_JSON_SAMPLE_LENGTH;
let large_json = format!(r#"{{"data": "{}"}}"#, "x".repeat(50_000));
let mut state = QueryState::new(large_json);
state.execute(".");
assert!(state.result.is_ok());
let context_cache = state.last_successful_result_for_context.as_ref().unwrap();
let unformatted = state.last_successful_result_unformatted.as_ref().unwrap();
assert!(
context_cache.len() < unformatted.len(),
"Context cache should be minified/truncated"
);
assert!(
context_cache.len() <= MAX_JSON_SAMPLE_LENGTH + 15,
"Context cache should be bounded"
);
}
#[test]
fn test_context_cache_updated_when_result_changes() {
let json = r#"{"name": "test", "value": 42}"#;
let mut state = QueryState::new(json.to_string());
let initial_context_cache = state.last_successful_result_for_context.clone();
assert!(initial_context_cache.is_some());
state.execute(".name");
assert!(state.result.is_ok());
let updated_context_cache = state.last_successful_result_for_context.clone();
assert!(updated_context_cache.is_some());
assert_ne!(
initial_context_cache, updated_context_cache,
"Context cache should update when result changes"
);
}
#[test]
fn test_new_populates_rendered_cache() {
let json = r#"{"name": "test"}"#;
let state = QueryState::new(json.to_string());
assert!(
state.last_successful_result_rendered.is_some(),
"new() should populate rendered cache"
);
}
#[test]
fn test_successful_query_updates_rendered_cache() {
let json = r#"{"name": "test", "value": 42}"#;
let mut state = QueryState::new(json.to_string());
state.execute(".name");
assert!(state.result.is_ok(), "Query should succeed");
assert!(
state.last_successful_result_rendered.is_some(),
"Rendered cache should be populated after successful query"
);
}
#[test]
fn test_error_query_preserves_rendered_cache() {
let json = r#"{"value": 42}"#;
let mut state = QueryState::new(json.to_string());
state.execute(".value");
let cached_rendered = state.last_successful_result_rendered.clone();
assert!(cached_rendered.is_some());
state.execute(".[invalid syntax");
assert!(state.result.is_err(), "Query should fail");
assert_eq!(
state.last_successful_result_rendered.is_some(),
cached_rendered.is_some(),
"Rendered cache should be preserved on error"
);
}
#[test]
fn test_null_result_preserves_rendered_cache() {
let json = r#"{"name": "test", "age": 30}"#;
let mut state = QueryState::new(json.to_string());
let initial_rendered = state.last_successful_result_rendered.clone();
assert!(initial_rendered.is_some());
state.execute(".nonexistent");
assert!(state.result.is_ok());
assert!(state.result.as_ref().unwrap().contains("null"));
assert_eq!(
state.last_successful_result_rendered.is_some(),
initial_rendered.is_some(),
"Rendered cache should be preserved for null-only results"
);
}
#[test]
fn test_line_count_always_uses_last_successful_result() {
let json = r#"{"test": true}"#;
let mut state = QueryState::new(json.to_string());
let multiline_result: String = (0..50).map(|i| format!("line{}\n", i)).collect();
state.last_successful_result = Some(Arc::new(multiline_result.clone()));
state.cached_line_count = multiline_result.lines().count() as u32;
state.result = Ok("null\n".to_string());
assert_eq!(state.line_count(), 50);
}
#[test]
fn test_max_line_width_always_uses_last_successful_result() {
let json = r#"{"test": true}"#;
let mut state = QueryState::new(json.to_string());
let wide_result = "short\nthis_is_a_very_long_line_with_many_characters\nshort";
state.last_successful_result = Some(Arc::new(wide_result.to_string()));
state.cached_max_line_width = wide_result.lines().map(|l| l.len()).max().unwrap_or(0) as u16;
state.result = Ok("x".to_string());
assert_eq!(state.max_line_width(), 45);
}
#[test]
fn test_execute_logs_parse_failure() {
let json = r#"{"a": 1, "b": 2}"#;
let mut state = QueryState::new(json.to_string());
state.execute("to_entries | .[] | \"\\(.key)=\\(.value)\"");
let result_str = state.result.as_ref().unwrap();
assert!(result_str.contains("a=1"));
}
#[test]
fn test_request_id_wraps_and_skips_zero() {
let json = r#"{"test": true}"#;
let mut state = QueryState::new(json.to_string());
state.next_request_id = u64::MAX;
state.execute_async(".");
assert_eq!(state.next_request_id, 1);
}
#[test]
fn test_execute_async_with_no_channel() {
let json = r#"{"test": true}"#;
let mut state = QueryState::new(json.to_string());
state.request_tx = None;
state.execute_async(".");
}
#[test]
fn test_poll_response_with_no_receiver() {
let json = r#"{"test": true}"#;
let mut state = QueryState::new(json.to_string());
state.response_rx = None;
let result = state.poll_response();
assert!(result.is_none());
}