use sdivi_config::PatternsConfig;
use sdivi_lang_typescript::TypeScriptAdapter;
use sdivi_parsing::adapter::LanguageAdapter;
use sdivi_parsing::feature_record::FeatureRecord;
use sdivi_patterns::build_catalog;
use std::path::{Path, PathBuf};
fn workspace_root() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.expect("crates/ parent must exist")
.parent()
.expect("workspace root must exist")
.to_path_buf()
}
fn read_and_parse_ts(rel_path: &str) -> FeatureRecord {
let abs = workspace_root().join(rel_path);
let source = std::fs::read_to_string(&abs)
.unwrap_or_else(|e| panic!("could not read TypeScript fixture {:?}: {e}", abs));
TypeScriptAdapter.parse_file(Path::new(rel_path), source)
}
fn catalog_config() -> PatternsConfig {
PatternsConfig {
min_pattern_nodes: 1,
..PatternsConfig::default()
}
}
#[test]
fn simple_typescript_fixture_produces_logging_bucket_after_m33() {
let records = vec![
read_and_parse_ts("tests/fixtures/simple-typescript/app.ts"),
read_and_parse_ts("tests/fixtures/simple-typescript/utils.ts"),
read_and_parse_ts("tests/fixtures/simple-typescript/models.ts"),
];
let call_expr_count: usize = records
.iter()
.flat_map(|r| &r.pattern_hints)
.filter(|h| h.node_kind == "call_expression")
.count();
assert!(
call_expr_count >= 1,
"TypeScript fixture must produce at least one call_expression hint; \
the sentinel test is only meaningful when the native pipeline actively classifies nodes."
);
let catalog = build_catalog(&records, &catalog_config());
assert!(
catalog.entries.contains_key("logging"),
"build_catalog MUST produce a `logging` bucket in M33 — `classify_hint` routes \
console.log(...) call_expression nodes to logging. Present categories: {:?}",
catalog.entries.keys().collect::<Vec<_>>()
);
let log_total: u32 = catalog.entries["logging"].values().map(|s| s.count).sum();
assert!(
log_total >= 1,
"`logging` bucket must contain at least one instance, got {log_total}"
);
}
#[test]
fn simple_typescript_fixture_data_access_requires_matching_callee() {
let records = vec![
read_and_parse_ts("tests/fixtures/simple-typescript/app.ts"),
read_and_parse_ts("tests/fixtures/simple-typescript/utils.ts"),
];
let catalog = build_catalog(&records, &catalog_config());
assert!(
catalog.entries.contains_key("data_access"),
"data_access must be present — fetch(...) in app.ts must match the data_access regex. \
Present categories: {:?}",
catalog.entries.keys().collect::<Vec<_>>()
);
let total_call_exprs: usize = records
.iter()
.flat_map(|r| &r.pattern_hints)
.filter(|h| h.node_kind == "call_expression")
.count();
let da_count: u32 = catalog.entries["data_access"]
.values()
.map(|s| s.count)
.sum();
assert!(
(da_count as usize) < total_call_exprs,
"data_access instance count ({da_count}) must be less than total call_expression \
count ({total_call_exprs}) — classify_hint drops unrecognised callees"
);
}