LeVCS/crates/levcs-cli/tests/inspect.rs

138 lines
4.9 KiB
Rust

//! End-to-end test for `levcs inspect`.
//!
//! Boots an in-process instance, pushes a repo with a small tree, then
//! drives the `levcs inspect` binary against it. Verifies the output
//! lists the current authority, branch heads, and the requested tree
//! contents (§7.3.5).
use std::net::SocketAddr;
use std::path::{Path, PathBuf};
use std::process::Command;
use levcs_instance::{router, AppState, InstanceConfig};
fn levcs_bin() -> String {
env!("CARGO_BIN_EXE_levcs").to_string()
}
fn run(args: &[&str], cwd: &Path, xdg: &Path) -> (i32, String, String) {
let out = Command::new(levcs_bin())
.args(args)
.current_dir(cwd)
.env("XDG_CONFIG_HOME", xdg)
.output()
.expect("run levcs");
(
out.status.code().unwrap_or(-1),
String::from_utf8_lossy(&out.stdout).to_string(),
String::from_utf8_lossy(&out.stderr).to_string(),
)
}
fn tempdir(prefix: &str) -> PathBuf {
let mut p = std::env::temp_dir();
let n = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_nanos())
.unwrap_or(0);
p.push(format!("{prefix}-{n}-{}", std::process::id()));
std::fs::create_dir_all(&p).unwrap();
p
}
async fn start_instance(root: PathBuf) -> (SocketAddr, tokio::task::JoinHandle<()>) {
let cfg = InstanceConfig {
root,
storage_mode: "full".into(),
federation_peers: Vec::new(),
allowed_handlers: Vec::new(),
mirrors: Vec::new(),
};
let state = AppState::new(cfg);
let app = router(state);
let listener = tokio::net::TcpListener::bind::<SocketAddr>("127.0.0.1:0".parse().unwrap())
.await
.unwrap();
let addr = listener.local_addr().unwrap();
let task = tokio::spawn(async move {
axum::serve(listener, app).await.ok();
});
(addr, task)
}
#[test]
fn inspect_lists_branches_authority_and_tree() {
let rt = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap();
let instance_root = tempdir("levcs-inspect-inst");
let instance_root_for_task = instance_root.clone();
let (addr, task) = rt.block_on(async move { start_instance(instance_root_for_task).await });
let base_url = format!("http://{addr}/levcs/v1");
let source = tempdir("levcs-inspect-src");
let xdg = tempdir("levcs-inspect-cfg");
assert_eq!(run(&["init", "--key", "alice"], &source, &xdg).0, 0);
std::fs::write(source.join("README"), b"hello\n").unwrap();
std::fs::create_dir_all(source.join("nested")).unwrap();
std::fs::write(source.join("nested/file.txt"), b"deep\n").unwrap();
assert_eq!(run(&["track", "--all"], &source, &xdg).0, 0);
assert_eq!(run(&["commit", "-m", "first"], &source, &xdg).0, 0);
assert_eq!(run(&["instance", "--set", &base_url], &source, &xdg).0, 0);
assert_eq!(run(&["push"], &source, &xdg).0, 0);
// Pull the repo_id off disk so we can inspect it remotely.
let genesis_hex = std::fs::read_to_string(source.join(".levcs/refs/authority/genesis"))
.unwrap()
.trim()
.to_string();
let genesis_path = source
.join(".levcs/objects")
.join(&genesis_hex[..2])
.join(&genesis_hex[2..]);
let bytes = std::fs::read(&genesis_path).unwrap();
let signed = levcs_core::object::SignedObject::parse(&bytes).unwrap();
let body = levcs_identity::authority::AuthorityBody::parse(&signed.body).unwrap();
let repo_id_hex = body.repo_id.to_hex();
// Inspect from a fresh directory so we're really hitting the
// network path. No `--from`; we'll set the active instance there.
let probe = tempdir("levcs-inspect-probe");
assert_eq!(run(&["instance", "--set", &base_url], &probe, &xdg).0, 0);
// Root inspect: must list the branch tip and the tree at root.
let (code, stdout, _e) = run(&["inspect", &repo_id_hex], &probe, &xdg);
assert_eq!(code, 0, "inspect at root must succeed");
assert!(stdout.contains("repo_id"), "must show repo_id: {stdout}");
assert!(
stdout.contains("current authority"),
"must show authority: {stdout}"
);
assert!(stdout.contains("branches:"), "must list branches: {stdout}");
assert!(stdout.contains("main"), "must show main branch: {stdout}");
assert!(
stdout.contains("README"),
"must list README at root: {stdout}"
);
assert!(
stdout.contains("nested"),
"must list nested subtree at root: {stdout}"
);
// Path inspect: drill into the `nested/` subtree.
let (code, stdout, _e) = run(&["inspect", &repo_id_hex, "nested"], &probe, &xdg);
assert_eq!(code, 0, "inspect at nested/ must succeed");
assert!(
stdout.contains("file.txt"),
"must list nested/file.txt: {stdout}"
);
task.abort();
let _ = std::fs::remove_dir_all(&instance_root);
let _ = std::fs::remove_dir_all(&source);
let _ = std::fs::remove_dir_all(&xdg);
let _ = std::fs::remove_dir_all(&probe);
}