hephaestus/crates/heph/tests/cli.rs

97 lines
2.9 KiB
Rust
Raw Normal View History

//! CLI tests (tech-spec §9): run the real `heph` binary against a real `hephd`
//! over a unix socket, and assert output + side effects (export files).
use std::path::PathBuf;
use std::process::Command;
use std::thread;
use std::time::Duration;
use tokio::net::UnixListener;
use heph_core::{FixedClock, LocalStore};
use hephd::Daemon;
const NOW: i64 = 1_704_067_200_000;
/// Spawn a daemon on its own thread+runtime; return (socket, tempdir).
fn spawn_daemon() -> (PathBuf, tempfile::TempDir) {
let dir = tempfile::tempdir().unwrap();
let db = dir.path().join("heph.db");
let socket = dir.path().join("d.sock");
let store = LocalStore::open(&db, Box::new(FixedClock(NOW))).unwrap();
let socket_for_thread = socket.clone();
thread::spawn(move || {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
rt.block_on(async move {
let listener = UnixListener::bind(&socket_for_thread).unwrap();
let _ = Daemon::new(store).serve(listener).await;
});
});
for _ in 0..200 {
if socket.exists() {
break;
}
thread::sleep(Duration::from_millis(5));
}
(socket, dir)
}
/// Run the `heph` binary against `socket`; return (stdout, success).
fn heph(socket: &std::path::Path, args: &[&str]) -> (String, bool) {
let out = Command::new(env!("CARGO_BIN_EXE_heph"))
.arg("--socket")
.arg(socket)
.args(args)
.output()
.expect("run heph");
(
String::from_utf8_lossy(&out.stdout).into_owned(),
out.status.success(),
)
}
#[test]
fn task_then_next_shows_the_task() {
let (socket, _dir) = spawn_daemon();
let (out, ok) = heph(&socket, &["task", "Buy milk", "--attention", "red"]);
assert!(ok, "task create failed: {out}");
assert!(out.contains("Created task"), "{out}");
let (out, ok) = heph(&socket, &["next"]);
assert!(ok);
assert!(out.contains("[red]"), "{out}");
assert!(out.contains("Buy milk"), "{out}");
}
#[test]
fn next_on_empty_store_is_friendly() {
let (socket, _dir) = spawn_daemon();
let (out, ok) = heph(&socket, &["next"]);
assert!(ok);
assert!(out.contains("Nothing actionable"), "{out}");
}
#[test]
fn export_writes_markdown_files() {
let (socket, dir) = spawn_daemon();
heph(&socket, &["doc", "Roof log", "--body", "# Roof"]);
let export_dir = dir.path().join("export");
let (out, ok) = heph(&socket, &["export", export_dir.to_str().unwrap()]);
assert!(ok, "export failed: {out}");
assert!(out.contains("Exported 1 nodes"), "{out}");
// The doc landed as a .md file under doc/.
let docs: Vec<_> = std::fs::read_dir(export_dir.join("doc"))
.unwrap()
.filter_map(|e| e.ok())
.collect();
assert_eq!(docs.len(), 1);
let text = std::fs::read_to_string(docs[0].path()).unwrap();
assert!(text.contains("title: \"Roof log\""), "{text}");
}