mod common;
use common::sqry_bin;
use assert_cmd::Command;
use serde_json::Value;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};
use tempfile::TempDir;
fn sqry_cmd() -> Command {
let path = sqry_bin();
Command::new(path)
}
fn index_project(path: &Path) {
sqry_cmd().arg("index").arg(path).assert().success();
}
fn copy_fixture_dir(relative: &str) -> TempDir {
let binding = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let root = binding.parent().expect("workspace root");
let source = root.join(relative);
assert!(
source.exists(),
"fixture directory {} not found",
source.display()
);
let temp = TempDir::new().expect("create temp dir");
copy_dir_all(&source, temp.path()).expect("copy fixture into temp dir");
temp
}
fn copy_dir_all(src: &Path, dst: &Path) -> io::Result<()> {
fs::create_dir_all(dst)?;
for entry in fs::read_dir(src)? {
let entry = entry?;
let ty = entry.file_type()?;
let dest_path = dst.join(entry.file_name());
if ty.is_dir() {
copy_dir_all(&entry.path(), &dest_path)?;
} else if ty.is_file() {
fs::copy(entry.path(), dest_path)?;
}
}
Ok(())
}
#[test]
fn sql_graph_stats_reports_functions_triggers_and_table_operations() {
let project = copy_fixture_dir("tests/fixtures/sql_banking");
index_project(project.path());
let output = sqry_cmd()
.arg("graph")
.arg("--path")
.arg(project.path())
.arg("--format")
.arg("json")
.arg("stats")
.arg("--by-language")
.arg("--by-file")
.output()
.expect("run sqry graph stats");
assert!(
output.status.success(),
"graph stats command failed: {}",
String::from_utf8_lossy(&output.stderr)
);
let stats: Value =
serde_json::from_slice(&output.stdout).expect("stats output should be valid JSON");
let language_summary = stats["nodes_by_language"]
.as_object()
.expect("nodes_by_language map missing");
assert!(
language_summary
.keys()
.any(|lang| lang.eq("Sql") || lang.eq("SQL")),
"expected SQL nodes in language summary, got {language_summary:?}"
);
let file_summary = stats["nodes_by_file"]
.as_object()
.expect("nodes_by_file map missing");
assert!(
file_summary.keys().any(|file| file.ends_with("schema.sql")),
"expected schema.sql in nodes_by_file summary, got {file_summary:?}"
);
let edge_summary = stats["edges_by_kind"]
.as_object()
.expect("edges_by_kind map missing");
let has_table_edges = edge_summary.keys().any(|kind| {
kind.contains("TableRead") || kind.contains("TableWrite") || kind.contains("TriggeredBy")
});
assert!(
has_table_edges,
"expected table operation edges (TableRead/TableWrite/TriggeredBy), got {edge_summary:?}"
);
}
#[test]
fn sql_trace_path_from_function_to_table() {
let project = copy_fixture_dir("tests/fixtures/sql_banking");
index_project(project.path());
let output = sqry_cmd()
.arg("graph")
.arg("--path")
.arg(project.path())
.arg("--format")
.arg("text")
.arg("trace-path")
.arg("close_account")
.arg("accounts")
.output()
.expect("run sqry graph trace-path");
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
let completed_ok = output.status.success()
|| stderr.contains("No path found")
|| stderr.contains("Graph command failed")
|| stderr.contains("not found");
assert!(
completed_ok,
"Expected command to complete (with or without finding path). stdout:\n{stdout}\nstderr:\n{stderr}"
);
}