This repository has been archived on 2026-10-08. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
maestro/crates/maestro-daemon/tests/sidecar.rs
T
naps62-yolo 946aa384a0
ci / rust (push) Failing after 3s
feat(maestro): add daemon tmux/sidecar/worktree primitives
Local primitives for `maestrod`, no network code yet:

- `tmux`: subprocess wrapper around new-session, kill-session,
  list-sessions (filtered to maestro-*), send-keys -l, pipe-pane -o,
  resize-window, kill-server.
- `sidecar`: atomic JSON metadata store keyed by session UUID, with
  load_all for daemon restart recovery.
- `worktree`: `git worktree add -b` with collision-proof generated paths
  and `git worktree remove --force` for cleanup_on_kill.

Integration tests use real tmux (isolated via unique socket + temp
TMUX_TMPDIR) and real git (tmpdir repo).

Closes NAP-212.
2026-05-24 13:02:41 +00:00

108 lines
3.1 KiB
Rust

//! Round-trip and recovery tests for the on-disk sidecar session store.
mod common;
use std::collections::HashMap;
use std::path::PathBuf;
use chrono::{DateTime, Utc};
use maestro_daemon::sidecar::SidecarStore;
use maestro_protocol::{Session, SessionStatus};
use serde_json::Value;
use tempfile::TempDir;
use uuid::Uuid;
fn sample(id: Uuid, daemon_id: &str) -> Session {
Session {
id,
daemon_id: daemon_id.to_string(),
cwd: PathBuf::from("/tmp/work"),
command: "bash".into(),
env: HashMap::from([("FOO".into(), "bar".into())]),
metadata: HashMap::from([("label".into(), Value::String("personal/maestro".into()))]),
status: SessionStatus::Running,
created_at: DateTime::parse_from_rfc3339("2026-05-24T12:00:00Z")
.unwrap()
.with_timezone(&Utc),
}
}
#[test]
fn write_then_read_round_trip() {
let dir = TempDir::new().unwrap();
let store = SidecarStore::new(dir.path());
let id = Uuid::new_v4();
let s = sample(id, "laptop");
store.write(&s).expect("write");
let loaded = store.read(id).expect("read");
assert_eq!(loaded.as_ref(), Some(&s));
let path = store.path_for(id);
assert!(path.exists());
assert!(path.ends_with(format!("{id}.json")));
}
#[test]
fn read_missing_returns_none() {
let dir = TempDir::new().unwrap();
let store = SidecarStore::new(dir.path());
assert!(store.read(Uuid::new_v4()).unwrap().is_none());
}
#[test]
fn delete_removes_file_and_is_idempotent() {
let dir = TempDir::new().unwrap();
let store = SidecarStore::new(dir.path());
let id = Uuid::new_v4();
let s = sample(id, "laptop");
store.write(&s).unwrap();
assert!(store.path_for(id).exists());
store.delete(id).unwrap();
assert!(!store.path_for(id).exists());
// second delete is a no-op
store.delete(id).unwrap();
}
#[test]
fn load_all_recovers_every_sidecar_and_skips_garbage() {
let dir = TempDir::new().unwrap();
let store = SidecarStore::new(dir.path());
let s1 = sample(Uuid::new_v4(), "laptop");
let s2 = sample(Uuid::new_v4(), "dev-vm");
store.write(&s1).unwrap();
store.write(&s2).unwrap();
// garbage files in the directory should be ignored
std::fs::write(dir.path().join("not-json.txt"), b"hello").unwrap();
std::fs::write(dir.path().join("broken.json"), b"{not json}").unwrap();
let mut loaded = store.load_all().expect("load_all");
loaded.sort_by_key(|s| s.daemon_id.clone());
let mut expected = vec![s1, s2];
expected.sort_by_key(|s| s.daemon_id.clone());
assert_eq!(loaded, expected);
}
#[test]
fn load_all_returns_empty_when_directory_missing() {
let dir = TempDir::new().unwrap();
let store = SidecarStore::new(dir.path().join("does-not-exist"));
assert!(store.load_all().unwrap().is_empty());
}
#[test]
fn write_creates_parent_directory() {
let dir = TempDir::new().unwrap();
let nested = dir.path().join("a/b/c");
let store = SidecarStore::new(&nested);
let s = sample(Uuid::new_v4(), "laptop");
store.write(&s).expect("write");
assert!(nested.exists());
assert!(store.path_for(s.id).exists());
}