use assert_cmd::Command;
use predicates::str::contains;
use std::path::{Path, PathBuf};
use std::time::Instant;
use tempfile::TempDir;
const TS_FIXTURE: &str = "tests/fixtures/typescript-simple";
fn copy_dir_all(src: &Path, dest: &Path) {
std::fs::create_dir_all(dest).unwrap();
for entry in std::fs::read_dir(src).unwrap() {
let entry = entry.unwrap();
let src_path = entry.path();
let dest_path = dest.join(entry.file_name());
if src_path.is_dir() {
copy_dir_all(&src_path, &dest_path);
} else {
std::fs::copy(&src_path, &dest_path).unwrap();
}
}
}
fn setup_indexed_fixture() -> (TempDir, PathBuf) {
let dir = TempDir::new().unwrap();
let fixture = Path::new(TS_FIXTURE);
copy_dir_all(fixture, dir.path());
Command::cargo_bin("scope")
.unwrap()
.arg("init")
.current_dir(dir.path())
.assert()
.success();
Command::cargo_bin("scope")
.unwrap()
.args(["index", "--full"])
.current_dir(dir.path())
.assert()
.success();
let root = dir.path().to_path_buf();
(dir, root)
}
#[test]
fn test_incremental_detects_added_file() {
let (_dir, root) = setup_indexed_fixture();
let helper_dir = root.join("src").join("utils");
std::fs::create_dir_all(&helper_dir).unwrap();
std::fs::write(
helper_dir.join("helper.ts"),
"export function helper(value: string): string {\n return value.trim();\n}\n",
)
.unwrap();
Command::cargo_bin("scope")
.unwrap()
.arg("index")
.current_dir(&root)
.assert()
.success()
.stderr(contains("Added"))
.stderr(contains("helper.ts"));
Command::cargo_bin("scope")
.unwrap()
.args(["sketch", "helper"])
.current_dir(&root)
.assert()
.success()
.stdout(contains("helper"));
}
#[test]
fn test_incremental_detects_modified_file() {
let (_dir, root) = setup_indexed_fixture();
let logger_path = root.join("src").join("utils").join("logger.ts");
std::fs::write(
&logger_path,
"export class Logger {\n\
info(message: string): void {\n\
console.log(message);\n\
}\n\
\n\
error(message: string): void {\n\
console.error(message);\n\
}\n\
\n\
warn(message: string): void {\n\
console.warn(message);\n\
}\n\
}\n",
)
.unwrap();
Command::cargo_bin("scope")
.unwrap()
.arg("index")
.current_dir(&root)
.assert()
.success()
.stderr(contains("Modified"))
.stderr(contains("logger.ts"));
}
#[test]
fn test_incremental_detects_deleted_file() {
let (_dir, root) = setup_indexed_fixture();
let logger_path = root.join("src").join("utils").join("logger.ts");
std::fs::remove_file(&logger_path).unwrap();
Command::cargo_bin("scope")
.unwrap()
.arg("index")
.current_dir(&root)
.assert()
.success()
.stderr(contains("Deleted"))
.stderr(contains("logger.ts"));
}
#[test]
fn test_incremental_no_changes() {
let (_dir, root) = setup_indexed_fixture();
Command::cargo_bin("scope")
.unwrap()
.arg("index")
.current_dir(&root)
.assert()
.success()
.stderr(contains("up to date"));
}
#[test]
fn test_full_index_rebuilds_everything() {
let (_dir, root) = setup_indexed_fixture();
Command::cargo_bin("scope")
.unwrap()
.args(["index", "--full"])
.current_dir(&root)
.assert()
.success()
.stderr(contains("symbols"));
}
#[test]
fn test_incremental_performance() {
let (_dir, root) = setup_indexed_fixture();
let new_file = root.join("src").join("utils").join("perf_test_helper.ts");
std::fs::write(
&new_file,
"export function perfHelper(x: number): number {\n return x * 2;\n}\n",
)
.unwrap();
let start = Instant::now();
Command::cargo_bin("scope")
.unwrap()
.arg("index")
.current_dir(&root)
.assert()
.success();
let elapsed = start.elapsed();
assert!(
elapsed.as_secs_f64() < 2.0,
"incremental index of a single file should complete in < 2 s, took {:.2} s",
elapsed.as_secs_f64()
);
}