use std::path::{Path, PathBuf};
use std::process::Command;
use trusty_common::search_index::IndexRegistration;
use super::{BaseFacetOutcome, base_checkout_of, base_facet_options, ensure_base_facet_indexed};
struct EnvVarGuard {
key: &'static str,
prev: Option<String>,
}
impl EnvVarGuard {
fn set(key: &'static str, value: &str) -> Self {
let prev = std::env::var(key).ok();
unsafe {
std::env::set_var(key, value);
}
Self { key, prev }
}
}
impl Drop for EnvVarGuard {
fn drop(&mut self) {
unsafe {
match self.prev.take() {
Some(v) => std::env::set_var(self.key, v),
None => std::env::remove_var(self.key),
}
}
}
}
fn git_ok(dir: &Path, args: &[&str]) -> bool {
Command::new("git")
.arg("-C")
.arg(dir)
.args(args)
.output()
.map(|o| o.status.success())
.unwrap_or(false)
}
fn init_repo(base: &Path) -> Option<()> {
std::fs::create_dir_all(base).ok()?;
if !git_ok(base, &["init", "--initial-branch=main"]) {
return None;
}
let _ = git_ok(base, &["config", "user.email", "ci@test.invalid"]);
let _ = git_ok(base, &["config", "user.name", "CI"]);
std::fs::write(base.join("README.md"), "# fixture\n").ok()?;
if !git_ok(base, &["add", "."]) {
return None;
}
if !git_ok(base, &["commit", "-m", "seed"]) {
return None;
}
Some(())
}
struct Fixture {
_dir: tempfile::TempDir,
base: PathBuf,
worktree: PathBuf,
}
fn fixture() -> Option<Fixture> {
let dir = crate::test_support::hermetic_temp_dir();
let base = dir.path().join("base-repo");
init_repo(&base)?;
let worktree = dir.path().join("feature-wt");
if !git_ok(
&base,
&[
"worktree",
"add",
"-b",
"feature",
worktree.to_str().expect("utf8 path"),
],
) {
return None;
}
Some(Fixture {
base: base.canonicalize().ok()?,
worktree: worktree.canonicalize().ok()?,
_dir: dir,
})
}
#[test]
fn resolves_the_base_checkout_of_a_real_worktree() {
let Some(fx) = fixture() else {
eprintln!("skipping: git unavailable in this environment");
return;
};
assert_eq!(
base_checkout_of(&fx.worktree),
Some(fx.base.clone()),
"a worktree's gitdir pointer must lead back to the checkout it was cut from"
);
}
#[test]
#[serial_test::serial]
fn base_facet_targets_the_base_checkout_not_the_worktree() {
let Some(fx) = fixture() else {
eprintln!("skipping: git unavailable in this environment");
return;
};
let expected = trusty_common::derive_index_id(&trusty_common::resolve_project_root(&fx.base));
let worktree_id =
trusty_common::derive_index_id(&trusty_common::resolve_project_root(&fx.worktree));
assert_ne!(
expected, worktree_id,
"fixture precondition: the two facets must derive different ids"
);
match ensure_base_facet_indexed(&fx.worktree) {
BaseFacetOutcome::RegistrationUnconfirmed { index_id, reason } => {
assert_eq!(index_id, expected, "the base facet's id is the CHECKOUT's");
assert_eq!(reason, IndexRegistration::SkippedUnderTest);
}
other => panic!("expected an unconfirmed base-facet registration, got {other:?}"),
}
}
#[test]
fn base_facet_keeps_the_vector_lane_on() {
let opts = base_facet_options();
assert!(
!opts.skip_vector,
"the base checkout owns the embedding lane (#5060 ruling); skip_vector must stay false"
);
assert!(
!opts.allow_sensitive_path,
"the daemon's sensitive-path denylist stays armed (#2914)"
);
}
#[test]
fn plain_clone_is_not_a_base_facet_source() {
let dir = crate::test_support::hermetic_temp_dir();
let clone = dir.path().join("plain-clone");
std::fs::create_dir_all(clone.join(".git")).expect("mkdir .git");
assert_eq!(
ensure_base_facet_indexed(&clone),
BaseFacetOutcome::NotAWorktree
);
}
#[test]
fn unresolvable_gitdir_pointer_is_reported_not_registered() {
let dir = crate::test_support::hermetic_temp_dir();
let wt = dir.path().join("orphan-wt");
std::fs::create_dir_all(&wt).expect("mkdir");
std::fs::write(
wt.join(".git"),
"gitdir: /nonexistent-5069/base/.git/worktrees/orphan-wt\n",
)
.expect("write gitlink");
assert_eq!(base_checkout_of(&wt), None);
assert_eq!(
ensure_base_facet_indexed(&wt),
BaseFacetOutcome::BaseCheckoutUnresolved
);
}
#[test]
fn gitdir_outside_a_worktrees_directory_resolves_to_nothing() {
let dir = crate::test_support::hermetic_temp_dir();
let base = dir.path().join("super");
std::fs::create_dir_all(base.join(".git")).expect("mkdir .git");
let sub = base.join("vendor").join("dep");
std::fs::create_dir_all(&sub).expect("mkdir sub");
std::fs::write(
sub.join(".git"),
format!("gitdir: {}/.git/modules/dep\n", base.display()),
)
.expect("write gitlink");
assert_eq!(base_checkout_of(&sub), None);
}
fn fake_daemon() -> (std::net::SocketAddr, std::sync::mpsc::Receiver<String>) {
let listener = std::net::TcpListener::bind("127.0.0.1:0").expect("bind");
let addr = listener.local_addr().expect("addr");
let (tx, rx) = std::sync::mpsc::channel();
std::thread::spawn(move || {
use std::io::{Read, Write};
while let Ok((mut stream, _)) = listener.accept() {
let mut buf = [0u8; 8192];
let n = stream.read(&mut buf).unwrap_or(0);
let request = String::from_utf8_lossy(&buf[..n]).to_string();
let _ = stream.write_all(
b"HTTP/1.1 200 OK\r\ncontent-type: application/json\r\ncontent-length: 2\r\nconnection: close\r\n\r\n{}",
);
let _ = stream.flush();
if request.starts_with("POST /indexes ")
&& tx
.send(request.split("\r\n\r\n").nth(1).unwrap_or("").to_string())
.is_err()
{
return;
}
}
});
(addr, rx)
}
fn point_discovery_at(data_dir: &Path, addr: std::net::SocketAddr) -> (EnvVarGuard, EnvVarGuard) {
let search_data_dir = data_dir.join("trusty-search");
std::fs::create_dir_all(&search_data_dir).expect("mkdir");
std::fs::write(search_data_dir.join("http_addr"), addr.to_string()).expect("write http_addr");
(
EnvVarGuard::set(
trusty_common::data_dir::DATA_DIR_OVERRIDE_ENV,
&data_dir.to_string_lossy(),
),
EnvVarGuard::set(trusty_common::test_harness::ALLOW_PRODUCTION_ENV, "1"),
)
}
fn wait_for_create_index(
rx: &std::sync::mpsc::Receiver<String>,
root: &Path,
) -> Option<serde_json::Value> {
let deadline = std::time::Instant::now() + std::time::Duration::from_secs(10);
while let Some(remaining) = deadline.checked_duration_since(std::time::Instant::now()) {
let body = rx.recv_timeout(remaining).ok()?;
let parsed: serde_json::Value = serde_json::from_str(&body).ok()?;
if parsed.get("root_path").and_then(serde_json::Value::as_str)
== Some(root.to_string_lossy().as_ref())
{
return Some(parsed);
}
}
None
}
fn assert_base_facet_body(body: &serde_json::Value) {
assert_eq!(
body.get("skip_vector"),
Some(&serde_json::Value::Bool(false)),
"the base facet is the one index that embeds; got {body:?}"
);
assert_eq!(
body.get("allow_sensitive_path"),
Some(&serde_json::Value::Bool(false)),
"the daemon's denylist stays armed (#2914); got {body:?}"
);
}
#[test]
#[serial_test::serial]
fn base_facet_posts_the_base_checkout_with_the_vector_lane_on() {
let Some(fx) = fixture() else {
eprintln!("skipping: git unavailable in this environment");
return;
};
let data_dir = tempfile::tempdir().expect("data dir");
let (addr, rx) = fake_daemon();
let _guards = point_discovery_at(data_dir.path(), addr);
let _ = ensure_base_facet_indexed(&fx.worktree);
let expected_root = trusty_common::resolve_project_root(&fx.base);
let body = wait_for_create_index(&rx, &expected_root)
.expect("the fake daemon must have received a create-index POST for the BASE checkout");
assert_base_facet_body(&body);
}
#[test]
#[serial_test::serial]
fn session_launch_registers_the_base_facet_for_a_worktree() {
let Some(fx) = fixture() else {
eprintln!("skipping: git unavailable in this environment");
return;
};
let data_dir = tempfile::tempdir().expect("data dir");
let (addr, rx) = fake_daemon();
let _guards = point_discovery_at(data_dir.path(), addr);
let _ = crate::core::session_launch::register_project_index(&fx.worktree);
let expected_root = trusty_common::resolve_project_root(&fx.base);
let body = wait_for_create_index(&rx, &expected_root)
.expect("launching a session in a worktree must also register its base facet");
assert_base_facet_body(&body);
}
#[test]
#[serial_test::serial]
fn worktree_creation_registers_the_base_facet() {
let Some(fx) = fixture() else {
eprintln!("skipping: git unavailable in this environment");
return;
};
let data_dir = tempfile::tempdir().expect("data dir");
let (addr, rx) = fake_daemon();
let _guards = point_discovery_at(data_dir.path(), addr);
crate::core::worktree_index::index_new_worktree_in_background(fx.worktree.clone());
let expected_root = trusty_common::resolve_project_root(&fx.base);
let body = wait_for_create_index(&rx, &expected_root)
.expect("creating a worktree must also register its base facet");
assert_base_facet_body(&body);
}