use std::fs;
use std::io::Write;
use std::path::Path;
use tempfile::TempDir;
use crate::db::init_database;
use crate::manifest::{AgentInfo, AgentManifest, Capabilities, LlmConfig, Permissions, Resources, TalonRequirements};
use crate::orchestrator::{Orchestrator, SubTaskStatus};
use crate::resource_monitor::{ResourceEvent, ResourceLimits, ResourceMonitor};
fn write_file(dir: &Path, name: &str, content: &[u8]) {
let mut f = fs::File::create(dir.join(name)).unwrap();
f.write_all(content).unwrap();
}
#[test]
fn test_snapshot_modify_rollback_restores_files() {
let work = TempDir::new().unwrap();
let snap_dir = TempDir::new().unwrap();
write_file(work.path(), "data.txt", b"original content");
write_file(work.path(), "config.toml", b"[settings]\nvalue = 1");
for name in &["data.txt", "config.toml"] {
fs::copy(work.path().join(name), snap_dir.path().join(name)).unwrap();
}
write_file(work.path(), "data.txt", b"modified by agent");
write_file(work.path(), "new_output.txt", b"agent created this");
assert_eq!(fs::read(work.path().join("data.txt")).unwrap(), b"modified by agent");
assert!(work.path().join("new_output.txt").exists());
for name in &["data.txt", "config.toml"] {
fs::copy(snap_dir.path().join(name), work.path().join(name)).unwrap();
}
fs::remove_file(work.path().join("new_output.txt")).unwrap();
assert_eq!(fs::read(work.path().join("data.txt")).unwrap(), b"original content");
assert_eq!(fs::read(work.path().join("config.toml")).unwrap(), b"[settings]\nvalue = 1");
assert!(!work.path().join("new_output.txt").exists());
}
#[test]
fn test_snapshot_metadata_persisted_in_db() {
let db_file = TempDir::new().unwrap();
let db_path = db_file.path().join("hawk.db");
let conn = init_database(&db_path).unwrap();
conn.execute(
"INSERT INTO sessions (id, started_at, status) VALUES ('sess-snap', datetime('now'), 'Active')",
[],
).unwrap();
conn.execute(
"INSERT INTO snapshots (id, timestamp, agent_pid, task_description, file_count, strategy, working_dir, session_id) \
VALUES ('snap-001', datetime('now'), 1, 'pre-task', 2, 'file_copy', '/tmp/work', 'sess-snap')",
[],
).unwrap();
conn.execute(
"INSERT INTO snapshot_files (snapshot_id, file_path, hash, size_bytes) VALUES ('snap-001', 'data.txt', 'abc123', 42)",
[],
).unwrap();
let file_count: i64 = conn.query_row(
"SELECT file_count FROM snapshots WHERE id = 'snap-001'", [], |r| r.get(0),
).unwrap();
assert_eq!(file_count, 2);
let manifest_count: i64 = conn.query_row(
"SELECT COUNT(*) FROM snapshot_files WHERE snapshot_id = 'snap-001'", [], |r| r.get(0),
).unwrap();
assert_eq!(manifest_count, 1);
}
#[tokio::test]
async fn test_resource_monitor_memory_exceeded_triggers_event() {
let monitor = ResourceMonitor::new();
monitor.register(42, ResourceLimits { cpu_percent: 25, memory_mb: 512, max_open_fds: 64 });
let limit_bytes = 512u64 * 1024 * 1024;
let actual_bytes = 650u64 * 1024 * 1024;
monitor.event_tx.send(ResourceEvent::MemoryExceeded { pid: 42, memory_bytes: actual_bytes, limit_bytes }).unwrap();
let event = monitor.event_rx.lock().unwrap().try_recv().unwrap();
match event {
ResourceEvent::MemoryExceeded { pid, memory_bytes, limit_bytes: lim } => {
assert_eq!(pid, 42);
assert!(memory_bytes > lim);
}
}
}
#[test]
fn test_resource_monitor_deregister_after_suspension() {
let monitor = ResourceMonitor::new();
monitor.register(99, ResourceLimits { cpu_percent: 10, memory_mb: 256, max_open_fds: 32 });
assert!(monitor.limits.lock().unwrap().contains_key(&99));
monitor.deregister(99);
assert!(!monitor.limits.lock().unwrap().contains_key(&99));
}
#[test]
fn test_orchestration_subtask_assignment_and_completion() {
let mut orchestrator = Orchestrator::new();
orchestrator.register_agent(1, "research-agent", vec!["research".into(), "web-search".into()]);
orchestrator.register_agent(2, "coding-agent", vec!["coding".into(), "testing".into()]);
orchestrator.register_agent(3, "review-agent", vec!["review".into(), "analysis".into()]);
let plan = orchestrator.orchestrate("research the topic and write code").unwrap();
assert_eq!(plan.subtasks.len(), 2);
assert!(plan.dependencies.is_empty());
for subtask in &plan.subtasks {
assert!(subtask.assigned_agent.is_some());
}
let report = orchestrator.execute_plan(plan).unwrap();
assert!(report.success, "all sub-tasks should complete: {}", report.summary);
for subtask in &report.plan.subtasks {
assert_eq!(subtask.status, SubTaskStatus::Completed);
}
}
#[test]
fn test_orchestration_sequential_tasks_respect_dependencies() {
let mut orchestrator = Orchestrator::new();
orchestrator.register_agent(1, "research-agent", vec!["research".into()]);
orchestrator.register_agent(2, "coding-agent", vec!["coding".into()]);
let plan = orchestrator.orchestrate("research the topic then write code").unwrap();
assert_eq!(plan.subtasks.len(), 2);
assert!(plan.dependencies.contains(&(0, 1)));
let report = orchestrator.execute_plan(plan).unwrap();
assert!(report.success);
assert_eq!(report.plan.subtasks[0].status, SubTaskStatus::Completed);
assert_eq!(report.plan.subtasks[1].status, SubTaskStatus::Completed);
}
#[test]
fn test_orchestration_parallel_then_sequential() {
let mut orchestrator = Orchestrator::new();
orchestrator.register_agent(1, "research-agent", vec!["research".into()]);
orchestrator.register_agent(2, "coding-agent", vec!["coding".into()]);
orchestrator.register_agent(3, "review-agent", vec!["review".into()]);
let plan = orchestrator.orchestrate("research the topic and write code then review changes").unwrap();
assert_eq!(plan.subtasks.len(), 3);
let report = orchestrator.execute_plan(plan).unwrap();
assert!(report.success, "all sub-tasks should complete: {}", report.summary);
for subtask in &report.plan.subtasks {
assert_eq!(subtask.status, SubTaskStatus::Completed);
}
}