Fix continuation errors: auto-continue when final_output not called
- Add final_output_called flag to track if LLM properly completed - Auto-continue with prompt if tools executed but final_output missing - Remove unused last_action_was_tool and any_text_response variables - Simplifies previous complex incomplete response detection logic
This commit is contained in:
297
crates/g3-core/tests/test_session_continuation.rs
Normal file
297
crates/g3-core/tests/test_session_continuation.rs
Normal file
@@ -0,0 +1,297 @@
|
||||
//! Tests for session continuation functionality
|
||||
//!
|
||||
//! Note: These tests use serial execution because they modify the current directory
|
||||
|
||||
use g3_core::session_continuation::{
|
||||
SessionContinuation, clear_continuation, ensure_session_dir,
|
||||
get_latest_continuation_path, get_session_dir, has_valid_continuation,
|
||||
load_continuation, save_continuation,
|
||||
};
|
||||
use std::fs;
|
||||
use std::sync::Mutex;
|
||||
use tempfile::TempDir;
|
||||
|
||||
// Global mutex to ensure tests run serially (they modify current directory)
|
||||
static TEST_MUTEX: Mutex<()> = Mutex::new(());
|
||||
|
||||
/// Helper to set up a test environment with a temporary directory
|
||||
/// Returns the temp dir (must be kept alive) and the original directory
|
||||
fn setup_test_env() -> (TempDir, std::path::PathBuf) {
|
||||
let original_dir = std::env::current_dir().expect("Failed to get current dir");
|
||||
let temp_dir = TempDir::new().expect("Failed to create temp dir");
|
||||
std::env::set_current_dir(temp_dir.path()).expect("Failed to change to temp dir");
|
||||
(temp_dir, original_dir)
|
||||
}
|
||||
|
||||
/// Restore the original directory
|
||||
fn teardown_test_env(original_dir: std::path::PathBuf) {
|
||||
let _ = std::env::set_current_dir(original_dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_session_continuation_creation() {
|
||||
// This test doesn't need file system access
|
||||
let continuation = SessionContinuation::new(
|
||||
"test_session_123".to_string(),
|
||||
Some("Task completed successfully".to_string()),
|
||||
"/path/to/session.json".to_string(),
|
||||
45.0,
|
||||
Some("- [x] Task 1\n- [ ] Task 2".to_string()),
|
||||
"/home/user/project".to_string(),
|
||||
);
|
||||
|
||||
assert_eq!(continuation.session_id, "test_session_123");
|
||||
assert_eq!(
|
||||
continuation.final_output_summary,
|
||||
Some("Task completed successfully".to_string())
|
||||
);
|
||||
assert_eq!(continuation.context_percentage, 45.0);
|
||||
assert!(continuation.can_restore_full_context()); // 45% < 80%
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_can_restore_full_context_threshold() {
|
||||
// This test doesn't need file system access
|
||||
let test_cases = vec![
|
||||
(0.0, true),
|
||||
(50.0, true),
|
||||
(79.9, true),
|
||||
(80.0, false),
|
||||
(80.1, false),
|
||||
(95.0, false),
|
||||
(100.0, false),
|
||||
];
|
||||
|
||||
for (percentage, expected) in test_cases {
|
||||
let continuation = SessionContinuation::new(
|
||||
"test".to_string(),
|
||||
None,
|
||||
"path".to_string(),
|
||||
percentage,
|
||||
None,
|
||||
".".to_string(),
|
||||
);
|
||||
assert_eq!(
|
||||
continuation.can_restore_full_context(),
|
||||
expected,
|
||||
"Failed for percentage {}",
|
||||
percentage
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_save_and_load_continuation() {
|
||||
let _lock = TEST_MUTEX.lock().unwrap();
|
||||
let (temp_dir, original_dir) = setup_test_env();
|
||||
|
||||
let original = SessionContinuation::new(
|
||||
"save_load_test".to_string(),
|
||||
Some("Test summary content".to_string()),
|
||||
"/logs/g3_session_save_load_test.json".to_string(),
|
||||
35.5,
|
||||
Some("- [ ] Pending task".to_string()),
|
||||
temp_dir.path().to_string_lossy().to_string(),
|
||||
);
|
||||
|
||||
// Save the continuation
|
||||
let saved_path = save_continuation(&original).expect("Failed to save continuation");
|
||||
assert!(saved_path.exists());
|
||||
|
||||
// Load it back
|
||||
let loaded = load_continuation()
|
||||
.expect("Failed to load continuation")
|
||||
.expect("No continuation found");
|
||||
|
||||
assert_eq!(loaded.session_id, original.session_id);
|
||||
assert_eq!(loaded.final_output_summary, original.final_output_summary);
|
||||
assert_eq!(loaded.session_log_path, original.session_log_path);
|
||||
assert!((loaded.context_percentage - original.context_percentage).abs() < 0.01);
|
||||
assert_eq!(loaded.todo_snapshot, original.todo_snapshot);
|
||||
assert_eq!(loaded.working_directory, original.working_directory);
|
||||
|
||||
teardown_test_env(original_dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_load_continuation_when_none_exists() {
|
||||
let _lock = TEST_MUTEX.lock().unwrap();
|
||||
let (_temp_dir, original_dir) = setup_test_env();
|
||||
|
||||
// No continuation should exist in a fresh temp directory
|
||||
let result = load_continuation().expect("load_continuation should not error");
|
||||
assert!(result.is_none());
|
||||
|
||||
teardown_test_env(original_dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_clear_continuation() {
|
||||
let _lock = TEST_MUTEX.lock().unwrap();
|
||||
let (_temp_dir, original_dir) = setup_test_env();
|
||||
|
||||
// Create and save a continuation
|
||||
let continuation = SessionContinuation::new(
|
||||
"clear_test".to_string(),
|
||||
Some("Will be cleared".to_string()),
|
||||
"/path/to/session.json".to_string(),
|
||||
50.0,
|
||||
None,
|
||||
".".to_string(),
|
||||
);
|
||||
save_continuation(&continuation).expect("Failed to save");
|
||||
|
||||
// Verify it exists
|
||||
assert!(get_latest_continuation_path().exists());
|
||||
|
||||
// Clear it
|
||||
clear_continuation().expect("Failed to clear");
|
||||
|
||||
// Verify it's gone
|
||||
assert!(!get_latest_continuation_path().exists());
|
||||
|
||||
// Loading should return None
|
||||
let result = load_continuation().expect("load should not error");
|
||||
assert!(result.is_none());
|
||||
|
||||
teardown_test_env(original_dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ensure_session_dir_creates_directory() {
|
||||
let _lock = TEST_MUTEX.lock().unwrap();
|
||||
let (_temp_dir, original_dir) = setup_test_env();
|
||||
|
||||
let session_dir = get_session_dir();
|
||||
assert!(!session_dir.exists());
|
||||
|
||||
ensure_session_dir().expect("Failed to ensure session dir");
|
||||
|
||||
assert!(session_dir.exists());
|
||||
assert!(session_dir.is_dir());
|
||||
|
||||
teardown_test_env(original_dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_has_valid_continuation_with_missing_session_log() {
|
||||
let _lock = TEST_MUTEX.lock().unwrap();
|
||||
let (_temp_dir, original_dir) = setup_test_env();
|
||||
|
||||
// Create a continuation pointing to a non-existent session log
|
||||
let continuation = SessionContinuation::new(
|
||||
"invalid_test".to_string(),
|
||||
Some("Summary".to_string()),
|
||||
"/nonexistent/path/session.json".to_string(),
|
||||
30.0,
|
||||
None,
|
||||
".".to_string(),
|
||||
);
|
||||
save_continuation(&continuation).expect("Failed to save");
|
||||
|
||||
// Should be invalid because session log doesn't exist
|
||||
assert!(!has_valid_continuation());
|
||||
|
||||
teardown_test_env(original_dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_has_valid_continuation_with_existing_session_log() {
|
||||
let _lock = TEST_MUTEX.lock().unwrap();
|
||||
let (temp_dir, original_dir) = setup_test_env();
|
||||
|
||||
// Create a fake session log file
|
||||
let logs_dir = temp_dir.path().join("logs");
|
||||
fs::create_dir_all(&logs_dir).expect("Failed to create logs dir");
|
||||
let session_log_path = logs_dir.join("g3_session_valid_test.json");
|
||||
fs::write(&session_log_path, "{}").expect("Failed to write session log");
|
||||
|
||||
// Create a continuation pointing to the existing session log
|
||||
let continuation = SessionContinuation::new(
|
||||
"valid_test".to_string(),
|
||||
Some("Summary".to_string()),
|
||||
session_log_path.to_string_lossy().to_string(),
|
||||
30.0,
|
||||
None,
|
||||
temp_dir.path().to_string_lossy().to_string(),
|
||||
);
|
||||
save_continuation(&continuation).expect("Failed to save");
|
||||
|
||||
// Should be valid because session log exists
|
||||
assert!(has_valid_continuation());
|
||||
|
||||
teardown_test_env(original_dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_continuation_serialization_format() {
|
||||
let _lock = TEST_MUTEX.lock().unwrap();
|
||||
let (_temp_dir, original_dir) = setup_test_env();
|
||||
|
||||
let continuation = SessionContinuation::new(
|
||||
"format_test".to_string(),
|
||||
Some("Test summary".to_string()),
|
||||
"/path/to/session.json".to_string(),
|
||||
42.5,
|
||||
Some("- [x] Done\n- [ ] Todo".to_string()),
|
||||
"/workspace".to_string(),
|
||||
);
|
||||
save_continuation(&continuation).expect("Failed to save");
|
||||
|
||||
// Read the raw JSON and verify structure
|
||||
let json_content =
|
||||
fs::read_to_string(get_latest_continuation_path()).expect("Failed to read file");
|
||||
let parsed: serde_json::Value =
|
||||
serde_json::from_str(&json_content).expect("Failed to parse JSON");
|
||||
|
||||
assert_eq!(parsed["version"], "1.0");
|
||||
assert_eq!(parsed["session_id"], "format_test");
|
||||
assert_eq!(parsed["final_output_summary"], "Test summary");
|
||||
assert_eq!(parsed["session_log_path"], "/path/to/session.json");
|
||||
assert!((parsed["context_percentage"].as_f64().unwrap() - 42.5).abs() < 0.01);
|
||||
assert_eq!(parsed["todo_snapshot"], "- [x] Done\n- [ ] Todo");
|
||||
assert_eq!(parsed["working_directory"], "/workspace");
|
||||
assert!(parsed["created_at"].as_str().is_some()); // Should have a timestamp
|
||||
|
||||
teardown_test_env(original_dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiple_saves_overwrite() {
|
||||
let _lock = TEST_MUTEX.lock().unwrap();
|
||||
let (_temp_dir, original_dir) = setup_test_env();
|
||||
|
||||
// Save first continuation
|
||||
let first = SessionContinuation::new(
|
||||
"first_session".to_string(),
|
||||
Some("First summary".to_string()),
|
||||
"/path/first.json".to_string(),
|
||||
20.0,
|
||||
None,
|
||||
".".to_string(),
|
||||
);
|
||||
save_continuation(&first).expect("Failed to save first");
|
||||
|
||||
// Save second continuation (should overwrite)
|
||||
let second = SessionContinuation::new(
|
||||
"second_session".to_string(),
|
||||
Some("Second summary".to_string()),
|
||||
"/path/second.json".to_string(),
|
||||
60.0,
|
||||
None,
|
||||
".".to_string(),
|
||||
);
|
||||
save_continuation(&second).expect("Failed to save second");
|
||||
|
||||
// Load should return the second one
|
||||
let loaded = load_continuation()
|
||||
.expect("Failed to load")
|
||||
.expect("No continuation");
|
||||
assert_eq!(loaded.session_id, "second_session");
|
||||
assert_eq!(
|
||||
loaded.final_output_summary,
|
||||
Some("Second summary".to_string())
|
||||
);
|
||||
|
||||
teardown_test_env(original_dir);
|
||||
}
|
||||
Reference in New Issue
Block a user