#![allow(
clippy::unwrap_used,
clippy::expect_used,
clippy::unused_result_ok,
clippy::indexing_slicing,
clippy::redundant_clone
)]
use runtimo_core::{
capabilities::{FileRead, FileWrite, GitExec, Kill, ShellExec, Undo},
execute_with_telemetry, BackupManager, Capability, CapabilityRegistry, ProcessSnapshot,
Telemetry, WalEvent, WalEventType, WalReader, WalWriter,
};
use serde_json::json;
use std::fs;
use std::io::Write;
use std::path::PathBuf;
fn unique_test_dir() -> PathBuf {
let ns = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_nanos();
std::env::temp_dir().join(format!("runtimo_test_{}_{}", std::process::id(), ns))
}
fn wal_path(base: &std::path::Path) -> PathBuf {
let mut p = base.to_path_buf();
p.push("wal_dir/wal.jsonl");
p
}
fn backup_dir(base: &std::path::Path) -> PathBuf {
let mut p = base.to_path_buf();
p.push("backups");
p
}
fn setup() -> PathBuf {
let d = unique_test_dir();
fs::create_dir_all(&d).ok();
fs::create_dir_all(wal_path(&d).parent().unwrap()).ok();
d
}
fn cleanup(dir: &PathBuf) {
fs::remove_dir_all(dir).ok();
}
fn make_file(dir: &std::path::Path, name: &str, content: &str) -> PathBuf {
let p = dir.join(name);
let mut f = fs::File::create(&p).unwrap();
write!(f, "{}", content).unwrap();
p
}
fn ctx(id: impl Into<String>) -> runtimo_core::Context {
runtimo_core::Context {
dry_run: false,
job_id: id.into(),
working_dir: std::env::temp_dir(),
}
}
#[test]
fn reads_file_content() {
let dir = setup();
let p = make_file(&dir, "t.txt", "hello world");
let result = FileRead
.execute(&json!({ "path": p.to_str().unwrap() }), &ctx("r1"))
.unwrap();
assert_eq!(result.data["content"].as_str().unwrap(), "hello world");
cleanup(&dir);
}
#[test]
fn writes_file_content() {
let dir = setup();
let target = dir.join("w.txt");
let result = FileWrite::new(backup_dir(&dir))
.expect("Failed to create FileWrite")
.execute(
&json!({
"path": target.to_str().unwrap(),
"content": "test data"
}),
&ctx("w1"),
)
.unwrap();
assert!(result.success);
assert_eq!(fs::read_to_string(&target).unwrap(), "test data");
cleanup(&dir);
}
#[test]
fn executor_wraps_capability() {
let dir = setup();
let p = make_file(&dir, "e.txt", "executor test");
let result = execute_with_telemetry(
&FileRead,
&json!({ "path": p.to_str().unwrap() }),
false,
&wal_path(&dir),
)
.unwrap();
assert!(result.success);
assert_eq!(result.capability, "FileRead");
cleanup(&dir);
}
#[test]
fn captures_telemetry() {
let t = Telemetry::capture();
assert!(t.timestamp > 0);
assert!(!t.system.cpu_model.is_empty());
}
#[test]
fn captures_process_snapshot() {
let s = ProcessSnapshot::capture();
assert!(!s.processes.is_empty());
}
#[test]
fn registry_lists_capabilities() {
let dir = setup();
let reg = make_registry(&backup_dir(&dir));
let caps = reg.list();
assert_eq!(caps.len(), 2);
assert!(caps.contains(&"FileRead"));
assert!(reg.get("FileRead").is_some());
assert!(reg.get("NoSuchCap").is_none());
cleanup(&dir);
}
fn make_registry(bd: &std::path::Path) -> CapabilityRegistry {
let mut r = CapabilityRegistry::new();
r.register(FileRead);
r.register(FileWrite::new(bd.to_path_buf()).expect("Failed to create FileWrite"));
r
}
#[test]
fn rejects_path_traversal_read() {
assert!(FileRead
.validate(&json!({ "path": "../../../etc/passwd" }))
.is_err());
}
#[test]
fn rejects_path_traversal_write() {
let dir = setup();
let cap = FileWrite::new(backup_dir(&dir)).expect("Failed to create FileWrite");
assert!(cap
.validate(&json!({ "path": "../../../tmp/x.txt", "content": "x" }))
.is_err());
cleanup(&dir);
}
#[test]
fn rejects_reading_directory() {
let dir = setup();
assert!(FileRead
.validate(&json!({ "path": dir.to_str().unwrap() }))
.is_err());
cleanup(&dir);
}
#[test]
fn rejects_empty_path() {
let dir = setup();
assert!(FileRead.validate(&json!({ "path": "" })).is_err());
let cap = FileWrite::new(backup_dir(&dir)).expect("Failed to create FileWrite");
assert!(cap
.validate(&json!({ "path": "", "content": "x" }))
.is_err());
cleanup(&dir);
}
#[test]
fn reads_empty_file() {
let dir = setup();
let p = make_file(&dir, "empty.txt", "");
let r = FileRead
.execute(&json!({ "path": p.to_str().unwrap() }), &ctx("e1"))
.unwrap();
assert_eq!(r.data["content"].as_str().unwrap(), "");
cleanup(&dir);
}
#[test]
fn reads_unicode() {
let dir = setup();
let p = make_file(&dir, "uni.txt", "مرحبا 你好 🌍");
let r = FileRead
.execute(&json!({ "path": p.to_str().unwrap() }), &ctx("e2"))
.unwrap();
assert!(r.data["content"].as_str().unwrap().contains("مرحبا"));
cleanup(&dir);
}
#[test]
fn reads_large_file() {
let dir = setup();
let p = make_file(&dir, "big.txt", &"x".repeat(100_000));
let r = FileRead
.execute(&json!({ "path": p.to_str().unwrap() }), &ctx("e3"))
.unwrap();
assert_eq!(r.data["content"].as_str().unwrap().len(), 100_000);
cleanup(&dir);
}
#[test]
fn writes_unicode() {
let dir = setup();
let target = dir.join("uni_w.txt");
let content = "日本語 🔥 مرحبا";
FileWrite::new(backup_dir(&dir))
.expect("Failed to create FileWrite")
.execute(
&json!({
"path": target.to_str().unwrap(), "content": content
}),
&ctx("e4"),
)
.unwrap();
assert_eq!(fs::read_to_string(&target).unwrap(), content);
cleanup(&dir);
}
#[test]
fn creates_parent_directories() {
let dir = setup();
let deep = dir.join("a/b/c");
let target = deep.join("f.txt");
FileWrite::new(backup_dir(&dir))
.expect("Failed to create FileWrite")
.execute(
&json!({
"path": target.to_str().unwrap(), "content": "deep"
}),
&ctx("e5"),
)
.unwrap();
assert!(target.exists());
cleanup(&dir);
}
#[test]
fn check_disk_space_skips_when_parent_missing() {
let dir = setup();
let target = dir.join("x/y/z/deep.txt");
let result = FileWrite::new(backup_dir(&dir))
.expect("Failed to create FileWrite")
.execute(
&json!({
"path": target.to_str().unwrap(),
"content": "a"
}),
&ctx("g_edge_1"),
);
assert!(
result.is_ok(),
"Write to nonexistent parent failed: {:?}",
result
);
assert!(target.exists(), "File must exist after write");
assert_eq!(fs::read_to_string(&target).unwrap(), "a");
cleanup(&dir);
}
#[test]
fn check_disk_space_deep_nesting() {
let dir = setup();
let target = dir.join("a/b/c/d/e/file.txt");
FileWrite::new(backup_dir(&dir))
.expect("Failed to create FileWrite")
.execute(
&json!({
"path": target.to_str().unwrap(),
"content": "deep5"
}),
&ctx("g_edge_2"),
)
.unwrap();
assert!(target.exists());
assert_eq!(fs::read_to_string(&target).unwrap(), "deep5");
cleanup(&dir);
}
#[test]
fn check_disk_space_single_new_parent() {
let dir = setup();
let target = dir.join("newdir/file.txt");
FileWrite::new(backup_dir(&dir))
.expect("Failed to create FileWrite")
.execute(
&json!({
"path": target.to_str().unwrap(),
"content": "single"
}),
&ctx("g_edge_3"),
)
.unwrap();
assert!(target.exists());
assert_eq!(fs::read_to_string(&target).unwrap(), "single");
cleanup(&dir);
}
#[test]
fn check_disk_space_runs_when_parent_exists() {
let dir = setup();
make_file(&dir, "existing.txt", "old");
let result = FileWrite::new(backup_dir(&dir))
.expect("Failed to create FileWrite")
.execute(
&json!({
"path": dir.join("existing.txt").to_str().unwrap(),
"content": "new"
}),
&ctx("g_edge_4"),
);
assert!(result.is_ok());
assert_eq!(fs::read_to_string(dir.join("existing.txt")).unwrap(), "new");
cleanup(&dir);
}
#[test]
fn check_disk_space_empty_content_new_parent() {
let dir = setup();
let target = dir.join("newdir_empty/file.txt");
FileWrite::new(backup_dir(&dir))
.expect("Failed to create FileWrite")
.execute(
&json!({
"path": target.to_str().unwrap(),
"content": ""
}),
&ctx("g_edge_5"),
)
.unwrap();
assert!(target.exists());
assert_eq!(fs::read_to_string(&target).unwrap(), "");
cleanup(&dir);
}
#[test]
fn c4_ordering_concurrent_paths_same_parent() {
let dir = setup();
let parent = dir.join("shared_parent");
let paths: Vec<_> = (0..5)
.map(|i| parent.join(format!("sub{}/f{}.txt", i, i)))
.collect();
let fw = FileWrite::new(backup_dir(&dir)).expect("Failed to create FileWrite");
for (i, path) in paths.iter().enumerate() {
fw.execute(
&json!({
"path": path.to_str().unwrap(),
"content": format!("content_{}", i)
}),
&ctx(format!("c4_{}", i)),
)
.unwrap();
}
for (i, path) in paths.iter().enumerate() {
assert!(path.exists(), "Path {} must exist", path.display());
assert_eq!(fs::read_to_string(path).unwrap(), format!("content_{}", i));
}
cleanup(&dir);
}
#[test]
fn c4_ordering_write_after_parent_creation() {
let dir = setup();
let target = dir.join("order_test/sub/file.txt");
fs::create_dir_all(target.parent().unwrap()).unwrap();
let result = FileWrite::new(backup_dir(&dir))
.expect("Failed to create FileWrite")
.execute(
&json!({
"path": target.to_str().unwrap(),
"content": "after_parent"
}),
&ctx("c4_order"),
);
assert!(result.is_ok());
assert_eq!(fs::read_to_string(&target).unwrap(), "after_parent");
cleanup(&dir);
}
#[test]
fn g_sem_content_identity() {
let dir = setup();
let target = dir.join("identity.txt");
let content = "The quick brown fox jumps over the lazy dog";
FileWrite::new(backup_dir(&dir))
.expect("Failed to create FileWrite")
.execute(
&json!({
"path": target.to_str().unwrap(),
"content": content
}),
&ctx("sem1"),
)
.unwrap();
assert_eq!(fs::read_to_string(&target).unwrap(), content);
cleanup(&dir);
}
#[test]
fn g_sem_unicode_roundtrip() {
let dir = setup();
let target = dir.join("unicode.txt");
let content = "مرحبا世界🚀";
FileWrite::new(backup_dir(&dir))
.expect("Failed to create FileWrite")
.execute(
&json!({
"path": target.to_str().unwrap(),
"content": content
}),
&ctx("sem2"),
)
.unwrap();
assert_eq!(fs::read_to_string(&target).unwrap(), content);
cleanup(&dir);
}
#[test]
fn rejects_missing_file() {
assert!(FileRead
.validate(&json!({ "path": "/tmp/no_such_runtimo_file.txt" }))
.is_err());
}
#[test]
fn rejects_missing_field_in_args() {
let dir = setup();
assert!(FileRead.validate(&json!({ "wrong_field": "v" })).is_err());
let cap = FileWrite::new(backup_dir(&dir)).expect("Failed to create FileWrite");
assert!(cap.validate(&json!({ "path": "/tmp/x.txt" })).is_err()); cleanup(&dir);
}
#[test]
fn llmosafe_guard_reports_pressure() {
use runtimo_core::LlmoSafeGuard;
let guard = LlmoSafeGuard::new();
let p = guard.pressure();
assert!(p <= 100, "pressure should be 0-100, got {}", p);
}
#[test]
fn llmosafe_guard_reports_entropy() {
use runtimo_core::LlmoSafeGuard;
let guard = LlmoSafeGuard::new();
let e = guard.raw_entropy();
assert!(e <= 1000, "entropy should be 0-1000, got {}", e);
}
#[test]
fn llmosafe_guard_check_passes_on_idle_system() {
use runtimo_core::LlmoSafeGuard;
let guard = LlmoSafeGuard::new();
if let Err(e) = guard.check() {
eprintln!("System under pressure: {}", e);
}
}
#[test]
fn write_then_read_roundtrip() {
let dir = setup();
let target = dir.join("rt.txt");
let original = "roundtrip\nmulti-line 你好";
FileWrite::new(backup_dir(&dir))
.expect("Failed to create FileWrite")
.execute(
&json!({
"path": target.to_str().unwrap(), "content": original
}),
&ctx("rt1"),
)
.unwrap();
let r = FileRead
.execute(&json!({ "path": target.to_str().unwrap() }), &ctx("rt2"))
.unwrap();
assert_eq!(r.data["content"].as_str().unwrap(), original);
cleanup(&dir);
}
#[test]
fn backup_created_on_overwrite() {
let dir = setup();
let bd = backup_dir(&dir);
fs::create_dir_all(&bd).ok();
let target = dir.join("bk.txt");
FileWrite::new(bd.clone())
.expect("Failed to create FileWrite")
.execute(
&json!({
"path": target.to_str().unwrap(), "content": "original"
}),
&ctx("bk1"),
)
.unwrap();
let r = FileWrite::new(bd.clone())
.expect("Failed to create FileWrite")
.execute(
&json!({
"path": target.to_str().unwrap(), "content": "modified"
}),
&ctx("bk2"),
)
.unwrap();
assert!(r.success);
assert_eq!(fs::read_to_string(&target).unwrap(), "modified");
let bp = bd.join("bk2").join("bk.txt");
assert!(bp.exists());
assert_eq!(fs::read_to_string(&bp).unwrap(), "original");
BackupManager::new(bd.clone())
.expect("Failed to create BackupManager")
.restore(&bp, &target)
.unwrap();
assert_eq!(fs::read_to_string(&target).unwrap(), "original");
cleanup(&dir);
}
#[test]
fn wal_records_jobs() {
let dir = setup();
let wp = wal_path(&dir);
let p = make_file(&dir, "wl.txt", "wal test");
execute_with_telemetry(
&FileRead,
&json!({ "path": p.to_str().unwrap() }),
false,
&wp,
)
.unwrap();
let reader = WalReader::load(&wp).unwrap();
let events = reader.events();
assert!(events.len() >= 2);
assert!(events
.iter()
.any(|e| matches!(e.event_type, runtimo_core::WalEventType::JobStarted)));
assert!(events
.iter()
.any(|e| matches!(e.event_type, runtimo_core::WalEventType::JobCompleted)));
cleanup(&dir);
}
#[test]
fn dry_run_does_not_write() {
let dir = setup();
let target = dir.join("dry.txt");
FileWrite::new(backup_dir(&dir))
.expect("Failed to create FileWrite")
.execute(
&json!({
"path": target.to_str().unwrap(), "content": "nope"
}),
&runtimo_core::Context {
dry_run: true,
job_id: "dry1".into(),
working_dir: std::env::temp_dir(),
},
)
.unwrap();
assert!(!target.exists());
cleanup(&dir);
}
#[test]
fn append_mode() {
let dir = setup();
let target = dir.join("app.txt");
let bw = backup_dir(&dir);
FileWrite::new(bw.clone())
.expect("Failed to create FileWrite")
.execute(
&json!({
"path": target.to_str().unwrap(), "content": "line1\n"
}),
&ctx("a1"),
)
.unwrap();
FileWrite::new(bw)
.expect("Failed to create FileWrite")
.execute(
&json!({
"path": target.to_str().unwrap(), "content": "line2\n", "append": true
}),
&ctx("a2"),
)
.unwrap();
let c = fs::read_to_string(&target).unwrap();
assert!(c.contains("line1"));
assert!(c.contains("line2"));
cleanup(&dir);
}
#[test]
fn multiple_jobs_in_sequence() {
let dir = setup();
let wp = wal_path(&dir);
let target = dir.join("seq.txt");
execute_with_telemetry(
&FileWrite::new(backup_dir(&dir)).expect("Failed to create FileWrite"),
&json!({ "path": target.to_str().unwrap(), "content": "seq test" }),
false,
&wp,
)
.unwrap();
let r = execute_with_telemetry(
&FileRead,
&json!({ "path": target.to_str().unwrap() }),
false,
&wp,
)
.unwrap();
println!("Success: {}, Output: {:?}", r.success, r.output);
assert!(r.success, "FileRead failed: {:?}", r.output.message);
assert_eq!(
r.output.data["content"]
.as_str()
.unwrap_or("CONTENT_MISSING"),
"seq test"
);
assert!(WalReader::load(&wp).unwrap().events().len() >= 4);
cleanup(&dir);
}
#[test]
fn roundtrip_many_contents() {
let dir = setup();
let cases = vec![
String::new(),
"a".into(),
"hello world".into(),
"مرحبا".into(),
"x".repeat(10_000),
"line1\nline2".into(),
"special: <>&\"'\\".into(),
];
for (i, content) in cases.into_iter().enumerate() {
let target = dir.join(format!("r{}.txt", i));
FileWrite::new(backup_dir(&dir))
.expect("Failed to create FileWrite")
.execute(
&json!({
"path": target.to_str().unwrap(), "content": content
}),
&ctx(format!("r{}", i)),
)
.unwrap();
let r = FileRead
.execute(
&json!({ "path": target.to_str().unwrap() }),
&ctx(format!("rr{}", i)),
)
.unwrap();
assert_eq!(
r.data["content"].as_str().unwrap(),
content,
"roundtrip failed case {}",
i
);
}
cleanup(&dir);
}
#[test]
fn timestamps_monotonic() {
let t1 = Telemetry::capture();
std::thread::sleep(std::time::Duration::from_millis(10));
let t2 = Telemetry::capture();
assert!(t2.timestamp >= t1.timestamp);
}
#[test]
fn process_snapshot_consistent() {
let s = ProcessSnapshot::capture();
assert_eq!(s.summary.total_processes, s.processes.len());
for p in &s.processes {
assert!(p.cpu_percent >= 0.0);
assert!(p.mem_percent >= 0.0);
}
let actual_zombies = s
.processes
.iter()
.filter(|p| p.stat.starts_with('Z'))
.count();
assert_eq!(s.summary.zombie_count, actual_zombies);
}
#[test]
fn executor_always_returns_telemetry() {
let dir = setup();
let wp = wal_path(&dir);
let p = make_file(&dir, "te.txt", "t");
let r = execute_with_telemetry(
&FileRead,
&json!({ "path": p.to_str().unwrap() }),
false,
&wp,
)
.unwrap();
assert!(r.telemetry_before.timestamp > 0);
assert!(r.telemetry_after.timestamp > 0);
assert!(r.process_before.total_processes > 0);
cleanup(&dir);
}
#[test]
fn wal_events_sequential() {
let dir = setup();
let wp = wal_path(&dir);
let p = make_file(&dir, "ws.txt", "t");
for _ in 0..3 {
execute_with_telemetry(
&FileRead,
&json!({ "path": p.to_str().unwrap() }),
false,
&wp,
)
.unwrap();
}
assert!(WalReader::load(&wp).unwrap().events().len() >= 6);
cleanup(&dir);
}
#[test]
fn c2_synthetic_registry_enforces_path_security() {
use runtimo_core::capabilities::{FileWrite, GitExec, Kill, ShellExec, Undo};
let dir = setup();
let mut registry = CapabilityRegistry::new();
registry.register(FileRead);
registry.register(FileWrite::new(backup_dir(&dir)).expect("FileWrite"));
registry.register(GitExec::new(backup_dir(&dir)).expect("GitExec"));
registry.register(ShellExec);
registry.register(Kill);
registry.register(Undo);
for traversal in &[
"../../../etc/passwd",
"/etc/shadow",
"../.ssh/authorized_keys",
] {
let cap = registry.get("FileWrite").unwrap();
let result = cap.validate(&json!({ "path": traversal, "content": "x" }));
assert!(
result.is_err(),
"Synthetic registry must reject traversal: {}",
traversal
);
}
let fw = registry.get("FileWrite").unwrap();
let critical = fw.validate(&json!({
"path": "/root/.ssh/authorized_keys",
"content": "malicious key"
}));
assert!(
critical.is_err(),
"Synthetic registry must block critical files"
);
let valid = fw.validate(&json!({ "path": "tmp/valid.txt", "content": "ok" }));
assert!(
valid.is_err() || valid.is_ok(),
"Validation should not panic on relative paths"
);
cleanup(&dir);
}
#[test]
fn c2_synthetic_registry_blocks_dangerous_commands() {
let dir = setup();
let mut registry = CapabilityRegistry::new();
registry.register(ShellExec);
registry.register(Kill);
let ctx = runtimo_core::Context {
dry_run: false,
job_id: "c2-test".into(),
working_dir: std::env::current_dir().unwrap_or_default(),
};
let se = registry.get("ShellExec").unwrap();
let result = se.execute(&json!({ "cmd": "mkfs", "timeout_secs": 1 }), &ctx);
assert!(
result.is_err(),
"Synthetic registry must block mkfs: {:?}",
result
);
let kill_cap = registry.get("Kill").unwrap();
assert!(
kill_cap
.execute(&json!({ "pid": 1, "signal": 9 }), &ctx)
.is_err(),
"Must protect PID 1"
);
cleanup(&dir);
}
#[test]
fn c3_wal_event_semantic_roundtrip() {
use runtimo_core::{Context, WalEvent, WalEventType, WalReader, WalWriter};
let dir = setup();
let wp = wal_path(&dir);
let ts = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
{
let mut wal = WalWriter::create(&wp).expect("create WAL");
let events = vec![
WalEvent {
seq: 1,
ts,
event_type: WalEventType::JobStarted,
job_id: "c3-test-001".into(),
capability: Some("FileWrite".into()),
output: Some(serde_json::json!({"message": "test"})),
error: None,
telemetry_before: None,
telemetry_after: None,
process_before: None,
process_after: None,
cmd: None,
cmd_stdout: None,
cmd_stderr: None,
cmd_exit_code: None,
cmd_corrected: None,
},
WalEvent {
seq: 2,
ts: ts + 1,
event_type: WalEventType::JobCompleted,
job_id: "c3-test-001".into(),
capability: Some("FileWrite".into()),
output: Some(serde_json::json!({"success": true})),
error: None,
telemetry_before: None,
telemetry_after: None,
process_before: None,
process_after: None,
cmd: None,
cmd_stdout: None,
cmd_stderr: None,
cmd_exit_code: None,
cmd_corrected: None,
},
WalEvent {
seq: 3,
ts: ts + 2,
event_type: WalEventType::JobFailed,
job_id: "c3-test-002".into(),
capability: Some("ShellExec".into()),
output: None,
error: Some("permission denied".into()),
telemetry_before: None,
telemetry_after: None,
process_before: None,
process_after: None,
cmd: Some("rm -rf /".into()),
cmd_stdout: None,
cmd_stderr: Some("Permission denied".into()),
cmd_exit_code: Some(1),
cmd_corrected: None,
},
];
for e in &events {
wal.append(e.clone()).expect("append");
}
}
let reader = WalReader::load(&wp).expect("read WAL");
let events = reader.events();
assert_eq!(events.len(), 3);
let started = &events[0];
assert_eq!(started.seq, 1);
assert!(matches!(started.event_type, WalEventType::JobStarted));
assert_eq!(started.job_id, "c3-test-001");
assert_eq!(started.capability.as_deref(), Some("FileWrite"));
assert!(
started.error.is_none(),
"error must be None for successful start"
);
let completed = &events[1];
assert_eq!(completed.seq, 2);
assert!(matches!(completed.event_type, WalEventType::JobCompleted));
assert_eq!(completed.job_id, "c3-test-001"); assert!(completed.output.is_some());
assert_eq!(completed.output.as_ref().unwrap()["success"], true);
let failed = &events[2];
assert!(matches!(failed.event_type, WalEventType::JobFailed));
assert!(failed.error.is_some());
assert!(failed.error.as_deref().unwrap().contains("denied"));
assert_eq!(failed.cmd.as_deref(), Some("rm -rf /"));
assert_eq!(failed.cmd_exit_code, Some(1));
assert_eq!(failed.cmd_corrected, None);
assert_eq!(failed.job_id, "c3-test-002");
assert_ne!(
failed.job_id, started.job_id,
"Different jobs must have distinct IDs"
);
cleanup(&dir);
}
#[test]
fn c3_wal_seq_monotonic_across_writes() {
let dir = setup();
let wp = wal_path(&dir);
let mut wal = WalWriter::create(&wp).expect("create");
let initial_seq = wal.seq();
for i in 0..10 {
let before = wal.seq();
wal.append(WalEvent {
seq: before,
ts: 100 + i,
event_type: WalEventType::JobStarted,
job_id: format!("seq-{}", i),
capability: None,
output: None,
error: None,
telemetry_before: None,
telemetry_after: None,
process_before: None,
process_after: None,
cmd: None,
cmd_stdout: None,
cmd_stderr: None,
cmd_exit_code: None,
cmd_corrected: None,
})
.expect("append");
assert_eq!(wal.seq(), before + 1, "SEQ must be strictly monotonic");
}
assert_eq!(wal.seq(), initial_seq + 10);
cleanup(&dir);
}
#[test]
fn c5_concurrent_writes_no_data_loss() {
use std::thread;
let dir = setup();
let target = dir.join("concurrent.txt");
let bw = backup_dir(&dir);
std::fs::write(&target, "initial").ok();
let t1 = {
let target = target.clone();
let bw = bw.clone();
thread::spawn(move || {
for i in 0..5 {
FileWrite::new(bw.clone())
.expect("FileWrite")
.execute(
&json!({ "path": target.to_str().unwrap(), "content": format!("t1-{}", i) }),
&ctx(format!("cw1-{}", i)),
)
.ok();
}
})
};
let t2 = {
let target = target.clone();
let bw = bw.clone();
thread::spawn(move || {
for i in 0..5 {
FileWrite::new(bw.clone())
.expect("FileWrite")
.execute(
&json!({ "path": target.to_str().unwrap(), "content": format!("t2-{}", i) }),
&ctx(format!("cw2-{}", i)),
)
.ok();
}
})
};
t1.join().unwrap();
t2.join().unwrap();
let content = std::fs::read_to_string(&target).ok();
assert!(content.is_some(), "File must exist after concurrent writes");
assert!(!content.unwrap().is_empty(), "File must not be empty");
let backups_exist = std::fs::read_dir(&bw)
.ok()
.map(|entries| entries.count() > 0)
.unwrap_or(false);
assert!(
backups_exist,
"At least one backup must survive concurrent writes"
);
cleanup(&dir);
}
#[test]
fn c5_wal_size_linear_with_event_count() {
let dir = setup();
let wp = wal_path(&dir);
let write_n = |n: usize| {
let mut wal = WalWriter::create(&wp).expect("create");
for i in 0..n {
wal.append(WalEvent {
seq: wal.seq(),
ts: i as u64,
event_type: WalEventType::JobStarted,
job_id: format!("size-{}", i),
capability: None,
output: None,
error: None,
telemetry_before: None,
telemetry_after: None,
process_before: None,
process_after: None,
cmd: None,
cmd_stdout: None,
cmd_stderr: None,
cmd_exit_code: None,
cmd_corrected: None,
})
.ok();
}
std::fs::metadata(&wp).map(|m| m.len()).unwrap_or(0)
};
let size_5 = write_n(5);
assert!(size_5 > 0, "WAL must contain data after writes");
cleanup(&dir);
}
#[test]
fn dispatch_pipeline_job_started_and_completed_in_wal() {
let dir = setup();
let wp = wal_path(&dir);
let p = make_file(&dir, "dp.txt", "dispatch pipeline test");
let result = execute_with_telemetry(
&FileRead,
&json!({ "path": p.to_str().unwrap() }),
false,
&wp,
);
assert!(result.is_ok(), "Dispatch must succeed");
let r = result.unwrap();
assert!(r.success);
let reader = WalReader::load(&wp).expect("read WAL");
let events = reader.events();
assert!(
events.len() >= 2,
"WAL must contain JobStarted + JobCompleted"
);
let job_id = &r.job_id;
let started = events.iter().find(|e| {
e.job_id == *job_id && matches!(e.event_type, runtimo_core::WalEventType::JobStarted)
});
assert!(
started.is_some(),
"WAL must contain JobStarted for {}",
job_id
);
let completed = events.iter().find(|e| {
e.job_id == *job_id && matches!(e.event_type, runtimo_core::WalEventType::JobCompleted)
});
assert!(
completed.is_some(),
"WAL must contain JobCompleted for {}",
job_id
);
let s = started.unwrap();
let c = completed.unwrap();
assert!(
s.seq < c.seq,
"JobStarted (seq {}) must precede JobCompleted (seq {})",
s.seq,
c.seq
);
cleanup(&dir);
}
#[test]
fn dispatch_pipeline_multiple_jobs_have_unique_ids() {
let dir = setup();
let wp = wal_path(&dir);
let p = make_file(&dir, "uq.txt", "unique id test");
let mut ids = std::collections::HashSet::new();
for i in 0..5 {
let result = execute_with_telemetry(
&FileRead,
&json!({ "path": p.to_str().unwrap() }),
false,
&wp,
)
.expect("dispatch");
assert!(result.success);
assert!(
ids.insert(result.job_id.clone()),
"Job IDs must be unique across dispatches (collision at {})",
i
);
}
assert_eq!(ids.len(), 5);
let reader = WalReader::load(&wp).expect("read");
let events = reader.events();
assert!(
events.len() >= 10,
"WAL must have 2 events per job (5 jobs → ≥10 events)"
);
cleanup(&dir);
}