138 lines
4.9 KiB
Rust
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);
|
|
}
|