use sqry_core::graph::GraphBuilder;
use sqry_core::graph::unified::build::staging::StagingGraph;
use sqry_lang_typescript::relations::TypeScriptGraphBuilder;
use std::path::Path;
use tree_sitter::Tree;
fn parse_ts(source: &str) -> Tree {
let mut parser = tree_sitter::Parser::new();
parser
.set_language(&tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into())
.expect("set TypeScript language");
parser.parse(source, None).expect("parse TypeScript")
}
fn build_graph(source: &str) -> StagingGraph {
let tree = parse_ts(source);
let mut staging = StagingGraph::new();
let builder = TypeScriptGraphBuilder::default();
builder
.build_graph(&tree, source.as_bytes(), Path::new("test.ts"), &mut staging)
.expect("build_graph should not fail");
staging
}
fn has_edge_tag(staging: &StagingGraph, tag: &str) -> bool {
use sqry_core::graph::unified::build::staging::StagingOp;
staging
.operations()
.iter()
.any(|op| matches!(op, StagingOp::AddEdge { kind, .. } if kind.tag() == tag))
}
fn all_edge_tags(staging: &StagingGraph) -> Vec<String> {
use sqry_core::graph::unified::build::staging::StagingOp;
staging
.operations()
.iter()
.filter_map(|op| {
if let StagingOp::AddEdge { kind, .. } = op {
Some(kind.tag().to_string())
} else {
None
}
})
.collect()
}
#[test]
fn builder_new_with_custom_depth() {
let builder = TypeScriptGraphBuilder::new(8);
let tree = parse_ts("function foo() {}");
let mut staging = StagingGraph::new();
builder
.build_graph(
&tree,
b"function foo() {}",
Path::new("test.ts"),
&mut staging,
)
.expect("build_graph should not fail");
assert!(staging.stats().nodes_staged >= 1);
}
#[test]
fn builder_default_produces_nodes() {
let staging = build_graph("function hello(): string { return 'hi'; }");
assert!(
staging.stats().nodes_staged >= 1,
"Expected at least one node"
);
}
#[test]
fn named_import() {
let staging = build_graph("import { Foo } from './foo';");
assert!(
has_edge_tag(&staging, "imports"),
"Expected imports edge; got: {:?}",
all_edge_tags(&staging)
);
}
#[test]
fn default_import() {
let staging = build_graph("import Foo from './foo';");
assert!(
has_edge_tag(&staging, "imports"),
"Expected imports edge; got: {:?}",
all_edge_tags(&staging)
);
}
#[test]
fn namespace_import() {
let staging = build_graph("import * as Foo from './foo';");
assert!(
has_edge_tag(&staging, "imports"),
"Expected imports edge; got: {:?}",
all_edge_tags(&staging)
);
}
#[test]
fn side_effect_import_no_panic() {
let _staging = build_graph("import './polyfill';");
}
#[test]
fn named_export() {
let staging = build_graph("function foo() {} export { foo };");
assert!(
has_edge_tag(&staging, "exports"),
"Expected exports edge; got: {:?}",
all_edge_tags(&staging)
);
}
#[test]
fn default_export_function() {
let staging = build_graph("export default function bar() {}");
assert!(
staging.stats().nodes_staged >= 1,
"Expected at least one node for exported function"
);
}
#[test]
fn re_export_from_module() {
let _staging = build_graph("export { foo } from './other';");
}
#[test]
fn export_star_barrel() {
let _staging = build_graph("export * from './barrel';");
}
#[test]
fn class_inheritance() {
let staging = build_graph(
r"
class Animal {}
class Dog extends Animal {}
",
);
assert!(
has_edge_tag(&staging, "inherits"),
"Expected inherits edge; got: {:?}",
all_edge_tags(&staging)
);
}
#[test]
fn class_implements_interface() {
let staging = build_graph(
r"
interface Printable { print(): void; }
class Document implements Printable {
print() {}
}
",
);
assert!(
has_edge_tag(&staging, "implements"),
"Expected implements edge; got: {:?}",
all_edge_tags(&staging)
);
}
#[test]
fn interface_extends_interface() {
let staging = build_graph(
r"
interface Base { id: number; }
interface Extended extends Base { name: string; }
",
);
assert!(
staging.stats().nodes_staged >= 1,
"Expected at least one node for interface"
);
}
#[test]
fn class_field_typeof_edges() {
let staging = build_graph(
r"
class MyService {
name: string;
count: number;
}
",
);
assert!(
has_edge_tag(&staging, "type_of"),
"Expected type_of edge for class fields; got: {:?}",
all_edge_tags(&staging)
);
}
#[test]
fn interface_property_typeof_edges() {
let staging = build_graph(
r"
interface Config {
host: string;
port: number;
}
",
);
assert!(
has_edge_tag(&staging, "type_of"),
"Expected type_of edge for interface properties; got: {:?}",
all_edge_tags(&staging)
);
}
#[test]
fn type_alias_references_type() {
let _staging = build_graph("type MyString = string;");
}
#[test]
fn mapped_type_alias() {
let _staging = build_graph("type ReadOnly<T> = { readonly [K in keyof T]: T[K] };");
}
#[test]
fn conditional_type_alias() {
let _staging = build_graph("type IsString<T> = T extends string ? 'yes' : 'no';");
}
#[test]
fn union_type_alias() {
let _staging = build_graph("type StringOrNumber = string | number;");
}
#[test]
fn const_enum_declaration() {
let staging = build_graph(
r"
const enum Direction {
Up,
Down,
Left,
Right,
}
",
);
assert!(
staging.stats().nodes_staged >= 1,
"Expected at least one node for const enum"
);
}
#[test]
fn string_enum_declaration() {
let staging = build_graph(
r#"
enum Color {
Red = "RED",
Green = "GREEN",
Blue = "BLUE",
}
"#,
);
assert!(
staging.stats().nodes_staged >= 1,
"Expected at least one node for string enum"
);
}
#[test]
fn namespace_declaration() {
let staging = build_graph(
r"
namespace MyApp {
export function init() {}
}
",
);
assert!(
staging.stats().nodes_staged >= 1,
"Expected Module node for namespace"
);
}
#[test]
fn namespace_augmentation() {
let staging = build_graph(
r"
namespace Lib {
export function a() {}
}
namespace Lib {
export function b() {}
}
",
);
assert!(staging.stats().nodes_staged >= 1);
}
#[test]
fn const_declaration() {
let staging = build_graph("const MAX: number = 100;");
let _ = staging.stats();
}
#[test]
fn let_declaration() {
let _staging = build_graph("let counter: number = 0;");
}
#[test]
fn var_declaration() {
let _staging = build_graph("var legacy: string = 'old';");
}
#[test]
fn required_parameter_type_edge() {
let staging = build_graph("function greet(name: string): void {}");
assert!(
has_edge_tag(&staging, "type_of"),
"Expected type_of edge for parameter; got: {:?}",
all_edge_tags(&staging)
);
}
#[test]
fn optional_parameter_type_edge() {
let staging = build_graph("function maybe(x?: number): void {}");
let _ = staging.stats();
}
#[test]
fn rest_parameter_type_edge() {
let staging = build_graph("function rest(...args: string[]): void {}");
let _ = staging.stats();
}
#[test]
fn arrow_function_typed_param() {
let staging = build_graph("const fn = (x: number): number => x * 2;");
assert!(
has_edge_tag(&staging, "type_of"),
"Expected type_of edge for arrow function parameter; got: {:?}",
all_edge_tags(&staging)
);
}
#[test]
fn method_typed_params_and_return() {
let staging = build_graph(
r"
class Calc {
add(a: number, b: number): number {
return a + b;
}
}
",
);
assert!(
has_edge_tag(&staging, "type_of"),
"Expected type_of edge for method parameters; got: {:?}",
all_edge_tags(&staging)
);
}
#[test]
fn function_return_type_edge() {
let staging = build_graph("function getCount(): number { return 0; }");
assert!(
staging.stats().nodes_staged >= 1,
"Expected at least one node for function with return type"
);
}
#[test]
fn async_function_return_type_edge() {
let staging = build_graph("async function fetchData(): Promise<string> { return ''; }");
assert!(
staging.stats().nodes_staged >= 1,
"Expected at least one node for async function"
);
}
#[test]
fn fetch_http_request() {
let staging = build_graph(
r"
async function loadData() {
const res = await fetch('https://api.example.com/data');
return res.json();
}
",
);
assert!(
has_edge_tag(&staging, "http_request"),
"Expected http_request edge for fetch; got: {:?}",
all_edge_tags(&staging)
);
}
#[test]
fn express_route_registration() {
let staging = build_graph(
r"
const app = express();
app.get('/users', (req, res) => { res.json([]); });
",
);
let _ = staging.stats();
}
#[test]
fn constructor_call() {
let staging = build_graph(
r"
class Foo {}
function main() {
const f = new Foo();
}
",
);
assert!(
has_edge_tag(&staging, "calls"),
"Expected calls edge for constructor; got: {:?}",
all_edge_tags(&staging)
);
}
#[test]
fn function_call_edge() {
let staging = build_graph(
r"
function helper() {}
function caller() { helper(); }
",
);
assert!(
has_edge_tag(&staging, "calls"),
"Expected calls edge; got: {:?}",
all_edge_tags(&staging)
);
}
#[test]
fn awaited_function_call() {
let staging = build_graph(
r"
async function doWork() {}
async function main() { await doWork(); }
",
);
assert!(
has_edge_tag(&staging, "calls"),
"Expected calls edge for awaited call; got: {:?}",
all_edge_tags(&staging)
);
}
#[test]
fn scope_function() {
let staging = build_graph(
r"
function outer() {
const x = 1;
return x;
}
",
);
assert!(staging.stats().nodes_staged >= 1);
}
#[test]
fn scope_arrow_function() {
let staging = build_graph(
r"
const double = (n: number) => n * 2;
",
);
let _ = staging.stats();
}
#[test]
fn scope_method() {
let staging = build_graph(
r"
class Counter {
private count = 0;
increment() { this.count++; }
}
",
);
assert!(staging.stats().nodes_staged >= 1);
}
#[test]
fn scope_block() {
let staging = build_graph(
r"
function test() {
{
const scoped = 42;
}
}
",
);
assert!(staging.stats().nodes_staged >= 1);
}
#[test]
fn scope_if_branch() {
let staging = build_graph(
r"
function check(x: number) {
if (x > 0) {
const positive = true;
} else {
const positive = false;
}
}
",
);
assert!(staging.stats().nodes_staged >= 1);
}
#[test]
fn scope_for_loop() {
let staging = build_graph(
r"
function iterate() {
for (let i = 0; i < 10; i++) {
const step = i;
}
}
",
);
assert!(staging.stats().nodes_staged >= 1);
}
#[test]
fn scope_for_in_loop() {
let staging = build_graph(
r"
function forIn(obj: Record<string, number>) {
for (const key in obj) {
console.log(key);
}
}
",
);
assert!(staging.stats().nodes_staged >= 1);
}
#[test]
fn scope_for_of_loop() {
let staging = build_graph(
r"
function forOf(arr: number[]) {
for (const item of arr) {
console.log(item);
}
}
",
);
assert!(staging.stats().nodes_staged >= 1);
}
#[test]
fn scope_while_loop() {
let staging = build_graph(
r"
function whileLoop() {
let n = 10;
while (n > 0) {
n--;
}
}
",
);
assert!(staging.stats().nodes_staged >= 1);
}
#[test]
fn scope_do_while_loop() {
let staging = build_graph(
r"
function doWhile() {
let x = 0;
do {
x++;
} while (x < 5);
}
",
);
assert!(staging.stats().nodes_staged >= 1);
}
#[test]
fn scope_try_catch_finally() {
let staging = build_graph(
r"
function safe() {
try {
const a = 1;
} catch (err) {
const msg = String(err);
} finally {
const done = true;
}
}
",
);
assert!(staging.stats().nodes_staged >= 1);
}
#[test]
fn scope_switch_case() {
let staging = build_graph(
r"
function sw(val: number) {
switch (val) {
case 1:
const one = 'one';
break;
default:
const other = 'other';
}
}
",
);
assert!(staging.stats().nodes_staged >= 1);
}
#[test]
fn bind_pattern_array_destructure() {
let staging = build_graph(
r"
function test() {
const [a, b, c] = [1, 2, 3];
return a + b + c;
}
",
);
assert!(staging.stats().nodes_staged >= 1);
}
#[test]
fn bind_pattern_object_destructure() {
let staging = build_graph(
r"
function test() {
const { x, y } = { x: 1, y: 2 };
return x + y;
}
",
);
assert!(staging.stats().nodes_staged >= 1);
}
#[test]
fn bind_pattern_assignment_default() {
let staging = build_graph(
r"
function test() {
const { name = 'default' } = {};
return name;
}
",
);
assert!(staging.stats().nodes_staged >= 1);
}
#[test]
fn bind_pattern_rest_element_array() {
let staging = build_graph(
r"
function test() {
const [first, ...rest] = [1, 2, 3];
return rest;
}
",
);
assert!(staging.stats().nodes_staged >= 1);
}
#[test]
fn bind_pattern_rest_element_object() {
let staging = build_graph(
r"
function test() {
const { a, ...others } = { a: 1, b: 2 };
return others;
}
",
);
assert!(staging.stats().nodes_staged >= 1);
}
#[test]
fn bind_pattern_pair_pattern() {
let staging = build_graph(
r"
function test() {
const { x: myX, y: myY } = { x: 1, y: 2 };
return myX + myY;
}
",
);
assert!(staging.stats().nodes_staged >= 1);
}
#[test]
fn bind_pattern_nested_destructure() {
let staging = build_graph(
r"
function test() {
const { a: [first, second] } = { a: [1, 2] };
return first + second;
}
",
);
assert!(staging.stats().nodes_staged >= 1);
}
#[test]
fn arrow_function_single_param_no_parens() {
let staging = build_graph(
r"
const double = x => x * 2;
",
);
let _ = staging.stats();
}
#[test]
fn for_of_identifier_variable() {
let staging = build_graph(
r"
function test(arr: number[]) {
let item: number;
for (item of arr) {
console.log(item);
}
}
",
);
assert!(staging.stats().nodes_staged >= 1);
}
#[test]
fn catch_clause_parameter_binding() {
let staging = build_graph(
r"
function safe() {
try {
throw new Error('test');
} catch (err) {
console.error(err);
}
}
",
);
assert!(staging.stats().nodes_staged >= 1);
}