use std::fs;
use std::path::Path;
use tempfile::tempdir;
use scryer_db::{Project, ScryerDb, SymbolReference};
use scryer_engine::EngineService;
async fn indexed(
files: &[(&str, &str)],
) -> anyhow::Result<(tempfile::TempDir, EngineService, Project, ScryerDb)> {
let temp = tempdir()?;
let root = temp.path();
for (path, content) in files {
let full = root.join(path);
fs::create_dir_all(full.parent().unwrap())?;
fs::write(full, content)?;
}
let db = ScryerDb::new_in_memory().await?;
let project = {
let mut conn = db.lock().await;
Project::create()
.slug("multilang".to_string())
.root_path(root.to_string_lossy().to_string())
.default_branch("main".to_string())
.is_active(true)
.created_at("2026-10-07T00:00:00Z".to_string())
.updated_at("2026-10-07T00:00:00Z".to_string())
.exec(&mut *conn)
.await?
};
let service = EngineService::new(db.clone());
service.index_project(project.id, root).await?;
Ok((temp, service, project, db))
}
#[tokio::test]
async fn test_python_cross_file_resolution() -> anyhow::Result<()> {
let (temp, service, project, db) = indexed(&[
("util.py", "def helper(x):\n return x + 1\n"),
(
"main.py",
"from util import helper\n\ndef run():\n return helper(1)\n",
),
])
.await?;
let def = service
.resolve_definition(project.id, temp.path(), Path::new("main.py"), 4, 12)
.await?
.expect("python call should resolve");
assert_eq!(def.symbol_name, "helper");
assert_eq!(def.file_path, Path::new("util.py"));
let mut conn = db.lock().await;
let refs = SymbolReference::filter_by_project_id(project.id)
.exec(&mut *conn)
.await?;
assert!(
refs.iter().any(|r| r.role == "call"),
"python call references must be indexed: {refs:?}"
);
let edges = scryer_db::CodeGraphEdge::filter_by_project_id(project.id)
.exec(&mut *conn)
.await?;
assert!(
edges.iter().any(|e| e.edge_type == "calls"),
"python run() -> helper() call edge must exist"
);
Ok(())
}
#[tokio::test]
async fn test_typescript_cross_file_resolution() -> anyhow::Result<()> {
let (temp, service, project, db) = indexed(&[
(
"util.ts",
"export function helper(x: number): number {\n return x + 1;\n}\n",
),
(
"main.ts",
"import { helper } from './util';\n\nexport function run(): number {\n return helper(1);\n}\n",
),
])
.await?;
let def = service
.resolve_definition(project.id, temp.path(), Path::new("main.ts"), 4, 10)
.await?
.expect("typescript call should resolve");
assert_eq!(def.symbol_name, "helper");
assert_eq!(def.file_path, Path::new("util.ts"));
let mut conn = db.lock().await;
let refs = SymbolReference::filter_by_project_id(project.id)
.exec(&mut *conn)
.await?;
assert!(
refs.iter().any(|r| r.role == "call"),
"typescript call references must be indexed: {refs:?}"
);
let edges = scryer_db::CodeGraphEdge::filter_by_project_id(project.id)
.exec(&mut *conn)
.await?;
assert!(
edges.iter().any(|e| e.edge_type == "calls"),
"typescript run() -> helper() call edge must exist"
);
Ok(())
}
#[tokio::test]
async fn test_index_file_handles_python_and_typescript() -> anyhow::Result<()> {
let (temp, service, project, _db) = indexed(&[
("a.py", "def a():\n pass\n"),
("a.ts", "export const a = 1;\n"),
])
.await?;
let root = temp.path();
fs::write(
root.join("a.py"),
"def a():\n pass\n\ndef b():\n a()\n",
)?;
fs::write(
root.join("a.ts"),
"export const a = 1;\nexport const b = a;\n",
)?;
service
.index_file(project.id, root, Path::new("a.py"))
.await?;
service
.index_file(project.id, root, Path::new("a.ts"))
.await?;
Ok(())
}