#[cfg(feature = "semantic")]
use super::capabilities::SemanticSearchStatus;
#[test]
fn lsp_binary_exists_finds_via_env_override() {
let tempdir = std::env::temp_dir().join(format!(
"codelens-phase4a-lsp-{}",
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_nanos())
.unwrap_or(0)
));
std::fs::create_dir_all(&tempdir).expect("mkdir tempdir");
#[cfg(windows)]
let fake_binary = tempdir.join("phase4a-fake-lsp-server.cmd");
#[cfg(not(windows))]
let fake_binary = tempdir.join("phase4a-fake-lsp-server");
std::fs::write(&fake_binary, "").expect("touch fake binary");
let previous = std::env::var_os("CODELENS_LSP_PATH_EXTRA");
let extra_path = std::env::join_paths([tempdir.as_path()]).expect("join extra LSP search path");
unsafe {
std::env::set_var("CODELENS_LSP_PATH_EXTRA", &extra_path);
}
assert!(
codelens_engine::lsp_binary_exists("phase4a-fake-lsp-server"),
"env override fallback must resolve the dummy binary"
);
unsafe {
match previous {
Some(v) => std::env::set_var("CODELENS_LSP_PATH_EXTRA", v),
None => std::env::remove_var("CODELENS_LSP_PATH_EXTRA"),
}
}
let _ = std::fs::remove_file(&fake_binary);
let _ = std::fs::remove_dir(&tempdir);
}
#[test]
fn lsp_binary_exists_returns_false_for_unknown_binary() {
let unique = format!(
"phase4a-definitely-not-installed-{}",
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_nanos())
.unwrap_or(0)
);
assert!(
!codelens_engine::lsp_binary_exists(&unique),
"helper must not return true for a nonexistent binary"
);
}
#[cfg(feature = "semantic")]
#[test]
fn semantic_search_status_reason_strings_are_distinct() {
assert_eq!(SemanticSearchStatus::Available.reason_str(), None);
let reasons = [
SemanticSearchStatus::ModelAssetsUnavailable
.reason_str()
.unwrap(),
SemanticSearchStatus::NotInActiveSurface
.reason_str()
.unwrap(),
SemanticSearchStatus::IndexMissing.reason_str().unwrap(),
SemanticSearchStatus::FeatureDisabled.reason_str().unwrap(),
];
for (i, r) in reasons.iter().enumerate() {
for (j, s) in reasons.iter().enumerate() {
if i != j {
assert_ne!(
r, s,
"SemanticSearchStatus reasons at indices {i} and {j} must be distinct"
);
}
}
assert!(
!r.is_empty(),
"SemanticSearchStatus reason {i} must be non-empty"
);
}
}
#[cfg(feature = "semantic")]
#[test]
fn semantic_search_status_is_available_only_for_available_variant() {
assert!(SemanticSearchStatus::Available.is_available());
assert!(!SemanticSearchStatus::ModelAssetsUnavailable.is_available());
assert!(!SemanticSearchStatus::NotInActiveSurface.is_available());
assert!(!SemanticSearchStatus::IndexMissing.is_available());
assert!(!SemanticSearchStatus::FeatureDisabled.is_available());
}
#[cfg(feature = "semantic")]
#[test]
fn planner_readonly_and_builder_minimal_expose_semantic_search() {
use crate::tool_defs::{ToolProfile, ToolSurface, is_tool_in_surface};
for profile in [ToolProfile::PlannerReadonly, ToolProfile::BuilderMinimal] {
let surface = ToolSurface::Profile(profile);
assert!(
is_tool_in_surface("semantic_search", surface),
"{profile:?} must expose semantic_search (Phase 4a §capability-reporting AC4)"
);
assert!(
is_tool_in_surface("index_embeddings", surface),
"{profile:?} must expose index_embeddings (Phase 4a §capability-reporting AC4)"
);
}
}
#[test]
fn build_info_constants_are_populated() {
assert!(
!crate::build_info::BUILD_VERSION.is_empty(),
"BUILD_VERSION must match CARGO_PKG_VERSION and be non-empty"
);
assert!(
!crate::build_info::BUILD_GIT_SHA.is_empty(),
"BUILD_GIT_SHA must be non-empty (at minimum 'unknown')"
);
assert!(
!crate::build_info::BUILD_TIME.is_empty(),
"BUILD_TIME must be non-empty RFC 3339 UTC"
);
assert_eq!(
crate::build_info::BUILD_TIME.len(),
20,
"BUILD_TIME should be exactly 20 chars (RFC 3339 UTC)"
);
assert!(
crate::build_info::BUILD_TIME.ends_with('Z'),
"BUILD_TIME should end with Z (UTC marker)"
);
let _ = crate::build_info::build_git_dirty();
}