use std::path::{Path, PathBuf};
use ripvec_core::entry_points::{
CEntryDetector, EntryPoint, EntryPointDetector, EntryPointKind, GoEntryDetector,
JavaEntryDetector, JsEntryDetector, KotlinEntryDetector, PythonEntryDetector,
RustEntryDetector, detector_for, summarize_entry_point_kinds,
};
fn has(entries: &[EntryPoint], name: &str, kind: EntryPointKind) -> bool {
entries.iter().any(|e| e.name == name && e.kind == kind)
}
#[test]
fn rust_detects_pub_fn_main() {
let src = "
pub fn main() {
println!(\"hi\");
}
";
let entries = RustEntryDetector.detect(src, Path::new("/tmp/synth/src/main.rs"));
assert!(
has(&entries, "main", EntryPointKind::Main),
"expected Main entry for `pub fn main`, got {entries:?}"
);
}
#[test]
fn rust_detects_test_attribute() {
let src = "
#[test]
fn foo() {}
";
let entries = RustEntryDetector.detect(src, Path::new("/tmp/synth/src/lib.rs"));
assert!(
has(&entries, "foo", EntryPointKind::Test),
"expected Test entry for `#[test] fn foo`, got {entries:?}"
);
}
#[test]
fn rust_detects_no_mangle() {
let src = "
#[no_mangle]
pub extern \"C\" fn bar() {}
";
let entries = RustEntryDetector.detect(src, Path::new("/tmp/synth/src/lib.rs"));
assert!(
has(&entries, "bar", EntryPointKind::Ffi),
"expected Ffi entry for `#[no_mangle] pub extern \"C\" fn bar`, got {entries:?}"
);
}
#[test]
fn rust_detects_proc_macro() {
let src = "
#[proc_macro_derive(Foo)]
pub fn derive_foo(input: TokenStream) -> TokenStream { input }
";
let entries = RustEntryDetector.detect(src, Path::new("/tmp/synth/src/lib.rs"));
assert!(
has(&entries, "derive_foo", EntryPointKind::ProcMacro),
"expected ProcMacro entry for `#[proc_macro_derive(Foo)] pub fn derive_foo`, got {entries:?}"
);
}
#[test]
fn rust_detects_lib_rs_pub_fn_as_library_export() {
let src = "
pub fn public_api() {}
fn private_helper() {}
";
let entries = RustEntryDetector.detect(src, Path::new("/tmp/synth/src/lib.rs"));
assert!(
has(&entries, "public_api", EntryPointKind::LibraryExport),
"expected LibraryExport entry for pub fn in lib.rs, got {entries:?}"
);
assert!(
!has(&entries, "private_helper", EntryPointKind::LibraryExport),
"private_helper should not be LibraryExport, got {entries:?}"
);
}
#[test]
fn rust_detects_build_rs() {
let src = "
fn main() {
println!(\"cargo:rerun-if-changed=foo\");
}
";
let entries = RustEntryDetector.detect(src, Path::new("/tmp/synth/build.rs"));
assert!(
entries
.iter()
.any(|e| e.kind == EntryPointKind::BuildScript),
"expected BuildScript entry for build.rs, got {entries:?}"
);
assert!(
has(&entries, "main", EntryPointKind::Main),
"expected Main entry for build.rs `fn main`, got {entries:?}"
);
}
#[test]
fn python_detects_main_block() {
let src = "
def main():
pass
if __name__ == \"__main__\":
main()
";
let entries = PythonEntryDetector.detect(src, Path::new("/tmp/synth/app.py"));
assert!(
has(&entries, "__main__", EntryPointKind::Main),
"expected Main entry for `if __name__ == \"__main__\":` block, got {entries:?}"
);
}
#[test]
fn python_detects_test_function() {
let src = "
def test_addition():
assert 1 + 1 == 2
";
let entries = PythonEntryDetector.detect(src, Path::new("/tmp/synth/test_math.py"));
assert!(
has(&entries, "test_addition", EntryPointKind::Test),
"expected Test entry for `def test_addition` in test_math.py, got {entries:?}"
);
}
#[test]
#[allow(
non_snake_case,
reason = "test name matches briefing spec test:python_detects___all___export"
)]
fn python_detects___all___export() {
let src = "
__all__ = [\"foo\", \"bar\"]
def foo():
pass
def bar():
pass
def _internal():
pass
";
let entries = PythonEntryDetector.detect(src, Path::new("/tmp/synth/mypkg/api.py"));
assert!(
has(&entries, "foo", EntryPointKind::LibraryExport),
"expected LibraryExport entry for `foo` in __all__, got {entries:?}"
);
assert!(
has(&entries, "bar", EntryPointKind::LibraryExport),
"expected LibraryExport entry for `bar` in __all__, got {entries:?}"
);
assert!(
!has(&entries, "_internal", EntryPointKind::LibraryExport),
"_internal should not be LibraryExport, got {entries:?}"
);
}
#[test]
fn go_detects_func_main() {
let src = "
package main
func main() {
println(\"hi\")
}
";
let entries = GoEntryDetector.detect(src, Path::new("/tmp/synth/cmd/app/main.go"));
assert!(
has(&entries, "main", EntryPointKind::Main),
"expected Main entry for `func main` in `package main`, got {entries:?}"
);
}
#[test]
fn go_detects_test_function() {
let src = "
package mypkg
import \"testing\"
func TestFoo(t *testing.T) {
if 1 + 1 != 2 {
t.Fatal(\"math is broken\")
}
}
";
let entries = GoEntryDetector.detect(src, Path::new("/tmp/synth/mypkg/foo_test.go"));
assert!(
has(&entries, "TestFoo", EntryPointKind::Test),
"expected Test entry for `func TestFoo`, got {entries:?}"
);
}
#[test]
fn go_detects_exported_name() {
let src = "
package mypkg
func PublicHelper() int {
return 42
}
func privateHelper() int {
return 0
}
";
let entries = GoEntryDetector.detect(src, Path::new("/tmp/synth/mypkg/helpers.go"));
assert!(
has(&entries, "PublicHelper", EntryPointKind::LibraryExport),
"expected LibraryExport entry for `PublicHelper`, got {entries:?}"
);
assert!(
!has(&entries, "privateHelper", EntryPointKind::LibraryExport),
"privateHelper should not be LibraryExport, got {entries:?}"
);
}
#[test]
fn go_detects_init_function() {
let src = "
package mypkg
func init() {
setupSomething()
}
";
let entries = GoEntryDetector.detect(src, Path::new("/tmp/synth/mypkg/setup.go"));
assert!(
has(&entries, "init", EntryPointKind::Init),
"expected Init entry for `func init`, got {entries:?}"
);
}
#[test]
fn detector_for_returns_correct_implementor() {
let rust_detector = detector_for("rust").expect("rust detector exists");
let rust_entries = rust_detector.detect("fn main() {}\n", Path::new("/tmp/x/src/main.rs"));
assert!(
has(&rust_entries, "main", EntryPointKind::Main),
"rust detector should detect main, got {rust_entries:?}"
);
let py_detector = detector_for("python").expect("python detector exists");
let py_entries = py_detector.detect(
"if __name__ == \"__main__\":\n pass\n",
Path::new("/tmp/x/app.py"),
);
assert!(
has(&py_entries, "__main__", EntryPointKind::Main),
"python detector should detect dunder main, got {py_entries:?}"
);
let go_detector = detector_for("go").expect("go detector exists");
let go_entries = go_detector.detect(
"package main\nfunc main() {}\n",
Path::new("/tmp/x/main.go"),
);
assert!(
has(&go_entries, "main", EntryPointKind::Main),
"go detector should detect main, got {go_entries:?}"
);
assert!(
detector_for("rs").is_some(),
"detector_for(\"rs\") should return RustEntryDetector"
);
assert!(
detector_for("py").is_some(),
"detector_for(\"py\") should return PythonEntryDetector"
);
assert!(
detector_for("pyi").is_some(),
"detector_for(\"pyi\") should return PythonEntryDetector"
);
assert!(
detector_for("haskell").is_none(),
"detector_for(\"haskell\") should return None"
);
let _ = PathBuf::new();
}
#[test]
fn rust_pub_use_reexport_treated_as_library_export() {
let src = "
pub use crate::sub::Foo;
";
let entries = RustEntryDetector.detect(src, Path::new("/tmp/synth/src/lib.rs"));
assert!(
has(&entries, "Foo", EntryPointKind::LibraryExport),
"expected LibraryExport entry for `pub use crate::sub::Foo;`, got {entries:?}"
);
}
#[test]
fn rust_pub_use_glob_treated_as_reexport_seed() {
let src = "
pub use crate::sub::*;
";
let entries = RustEntryDetector.detect(src, Path::new("/tmp/synth/src/lib.rs"));
assert!(
entries
.iter()
.any(|e| e.kind == EntryPointKind::LibraryExport
&& (e.name == "crate::sub::*" || e.name.ends_with("::*"))),
"expected glob LibraryExport entry for `pub use crate::sub::*;`, got {entries:?}"
);
}
#[test]
fn rust_pub_use_nested_path_finds_target() {
let src = "
pub use crate::a::b::c::Item;
";
let entries = RustEntryDetector.detect(src, Path::new("/tmp/synth/src/lib.rs"));
assert!(
has(&entries, "Item", EntryPointKind::LibraryExport),
"expected LibraryExport entry named `Item` for `pub use crate::a::b::c::Item;`, got {entries:?}"
);
}
#[test]
fn rust_pub_use_braced_group_emits_each_item() {
let src = "
pub use mod_name::{Foo, Bar};
";
let entries = RustEntryDetector.detect(src, Path::new("/tmp/synth/src/lib.rs"));
assert!(
has(&entries, "Foo", EntryPointKind::LibraryExport),
"expected LibraryExport entry `Foo` from braced re-export, got {entries:?}"
);
assert!(
has(&entries, "Bar", EntryPointKind::LibraryExport),
"expected LibraryExport entry `Bar` from braced re-export, got {entries:?}"
);
}
#[test]
fn rust_pub_use_outside_lib_or_mod_rs_is_ignored() {
let src = "
pub use crate::sub::Foo;
";
let entries = RustEntryDetector.detect(src, Path::new("/tmp/synth/src/random_module.rs"));
assert!(
!has(&entries, "Foo", EntryPointKind::LibraryExport),
"pub use in non-lib/mod.rs file should not emit LibraryExport, got {entries:?}"
);
}
#[test]
fn rust_examples_main_treated_as_entry() {
let src = "
fn main() {
println!(\"example\");
}
";
let entries = RustEntryDetector.detect(src, Path::new("/tmp/synth/examples/foo.rs"));
assert!(
has(&entries, "main", EntryPointKind::Main),
"expected Main entry for examples/foo.rs `fn main`, got {entries:?}"
);
}
#[test]
fn rust_benches_main_treated_as_entry() {
let src = "
fn main() {
// benchmark harness
}
";
let entries = RustEntryDetector.detect(src, Path::new("/tmp/synth/benches/bench.rs"));
assert!(
has(&entries, "main", EntryPointKind::Main),
"expected Main entry for benches/bench.rs `fn main`, got {entries:?}"
);
}
#[test]
fn rust_criterion_benches_function_treated_as_entry() {
let src = "
pub fn benches(c: &mut Criterion) {
c.bench_function(\"x\", |b| b.iter(|| 1 + 1));
}
";
let entries = RustEntryDetector.detect(src, Path::new("/tmp/synth/benches/perf.rs"));
assert!(
entries.iter().any(|e| e.name == "benches"
&& matches!(e.kind, EntryPointKind::Main | EntryPointKind::LibraryExport)),
"expected entry for criterion `pub fn benches`, got {entries:?}"
);
}
#[test]
fn rust_tool_annotated_method_treated_as_entry() {
let src = "
impl Server {
#[tool(name = \"find_dead_code\", description = \"...\")]
async fn find_dead_code(&self, p: Parameters) -> Result<CallToolResult, Error> {
Ok(CallToolResult::default())
}
}
";
let entries = RustEntryDetector.detect(src, Path::new("/tmp/synth/src/tools.rs"));
assert!(
has(
&entries,
"find_dead_code",
EntryPointKind::FrameworkDispatched
),
"expected FrameworkDispatched entry for `#[tool(...)]` method, got {entries:?}"
);
}
#[test]
fn rust_tool_router_impl_block_methods_treated_as_entries() {
let src = "
#[tool_router]
impl Foo {
fn a(&self) {}
fn b(&self) {}
fn c(&self) {}
}
";
let entries = RustEntryDetector.detect(src, Path::new("/tmp/synth/src/tools.rs"));
assert!(
has(&entries, "a", EntryPointKind::FrameworkDispatched),
"expected FrameworkDispatched entry for `a` inside #[tool_router] impl, got {entries:?}"
);
assert!(
has(&entries, "b", EntryPointKind::FrameworkDispatched),
"expected FrameworkDispatched entry for `b` inside #[tool_router] impl, got {entries:?}"
);
assert!(
has(&entries, "c", EntryPointKind::FrameworkDispatched),
"expected FrameworkDispatched entry for `c` inside #[tool_router] impl, got {entries:?}"
);
}
#[test]
fn entry_point_kind_framework_dispatched_distinguishable() {
let fd = EntryPointKind::FrameworkDispatched;
assert_ne!(fd, EntryPointKind::Main);
assert_ne!(fd, EntryPointKind::LibraryExport);
assert_ne!(fd, EntryPointKind::Test);
assert_ne!(fd, EntryPointKind::Ffi);
assert_ne!(fd, EntryPointKind::ProcMacro);
assert_ne!(fd, EntryPointKind::Init);
assert_ne!(fd, EntryPointKind::BuildScript);
}
#[test]
fn dead_code_report_summary_includes_framework_dispatched() {
use std::collections::HashMap;
let mut counts: HashMap<EntryPointKind, usize> = HashMap::new();
counts.insert(EntryPointKind::FrameworkDispatched, 12);
counts.insert(EntryPointKind::Main, 1);
let summary = summarize_entry_point_kinds(&counts);
assert!(
summary
.iter()
.any(|s| s.contains("framework-dispatched") || s.contains("MCP")),
"summary must mention framework-dispatched MCP tools, got {summary:?}"
);
assert!(
summary.iter().any(|s| s.contains("12")),
"summary must include the count `12`, got {summary:?}"
);
}
#[test]
fn c_main_with_argc_argv_treated_as_main_entry() {
let src = "int main(int argc, char **argv) { return 0; }\n";
let entries = CEntryDetector.detect(src, Path::new("/tmp/corpus/src/server.c"));
assert!(
has(&entries, "main", EntryPointKind::Main),
"expected Main entry for `int main(int argc, char **argv)`, got {entries:?}"
);
}
#[test]
fn c_pointer_return_function_named_main_treated_as_main_entry() {
let src = "int *main(void) { return NULL; }\n";
let entries = CEntryDetector.detect(src, Path::new("/tmp/corpus/src/server.c"));
assert!(
has(&entries, "main", EntryPointKind::Main),
"expected Main entry for `int *main(void)`, got {entries:?}"
);
}
#[test]
fn c_function_returning_struct_pointer_not_treated_as_main() {
let src = "struct foo *make_foo(void) { return NULL; }\n";
let entries = CEntryDetector.detect(src, Path::new("/tmp/corpus/src/alloc.c"));
assert!(
!has(&entries, "make_foo", EntryPointKind::Main),
"make_foo should not be a Main entry, got {entries:?}"
);
}
#[test]
fn js_export_default_function_treated_as_library_export() {
let src = "export default function App() { return null; }\n";
let entries = JsEntryDetector.detect(src, Path::new("/tmp/corpus/src/App.js"));
assert!(
has(&entries, "App", EntryPointKind::LibraryExport),
"expected LibraryExport entry for `export default function App`, got {entries:?}"
);
}
#[test]
fn js_module_exports_treated_as_library_export() {
let src = "module.exports = { foo: bar };\n";
let entries = JsEntryDetector.detect(src, Path::new("/tmp/corpus/lib/index.js"));
assert!(
entries
.iter()
.any(|e| e.kind == EntryPointKind::LibraryExport),
"expected at least one LibraryExport entry for `module.exports = ...`, got {entries:?}"
);
}
#[test]
fn js_test_in_test_file_treated_as_test_entry() {
let src = "test(\"my feature\", () => { expect(1).toBe(1); });\n";
let entries = JsEntryDetector.detect(src, Path::new("/tmp/corpus/src/app.test.js"));
assert!(
entries.iter().any(|e| e.kind == EntryPointKind::Test),
"expected Test entry for `test(...)` in *.test.js, got {entries:?}"
);
}
#[test]
fn js_arrow_function_export_const_treated_as_library_export() {
let src = "export const Foo = () => null;\n";
let entries = JsEntryDetector.detect(src, Path::new("/tmp/corpus/src/Foo.js"));
assert!(
has(&entries, "Foo", EntryPointKind::LibraryExport),
"expected LibraryExport entry for `export const Foo = () => ...`, got {entries:?}"
);
}
#[test]
fn python_subdir_main_seeds_as_main() {
let src = "
def main():
print('running')
if __name__ == \"__main__\":
main()
";
let entries = PythonEntryDetector.detect(src, Path::new("/tmp/aurora/scripts/cli/foo.py"));
assert!(
has(&entries, "main", EntryPointKind::Main),
"expected Main entry for `def main` called from __main__ block in subdir, got {entries:?}"
);
}
#[test]
fn python_argparse_main_function_seeds_as_main() {
let src = "
import argparse
def main():
args = argparse.ArgumentParser().parse_args()
print(args)
if __name__ == \"__main__\":
main()
";
let entries = PythonEntryDetector.detect(src, Path::new("/tmp/synth/cli/run.py"));
assert!(
has(&entries, "__main__", EntryPointKind::Main),
"expected Main entry for __main__ block, got {entries:?}"
);
assert!(
has(&entries, "main", EntryPointKind::Main),
"expected Main entry for `def main` (argparse CLI function) called from __main__, got {entries:?}"
);
}
#[test]
fn python_click_decorated_main_seeds_as_main() {
let src = "
import click
@click.command()
def cli():
pass
if __name__ == \"__main__\":
cli()
";
let entries = PythonEntryDetector.detect(src, Path::new("/tmp/synth/scripts/cli.py"));
assert!(
has(&entries, "cli", EntryPointKind::Main),
"expected Main entry for @click.command() decorated `def cli`, got {entries:?}"
);
}
#[test]
fn python_real_world_aurora_load_bronze() {
let src = "
import argparse
def parse_args():
parser = argparse.ArgumentParser(description=\"Load data\")
return parser.parse_args()
def main():
args = parse_args()
print(args)
if __name__ == \"__main__\":
main()
";
let entries =
PythonEntryDetector.detect(src, Path::new("/tmp/aurora/scripts/cli/load_bronze.py"));
assert!(
has(&entries, "main", EntryPointKind::Main),
"expected Main entry for `def main` in aurora load_bronze fixture, got {entries:?}"
);
}
#[test]
fn python_test_files_in_subdirs_still_seed_as_test() {
let src = "
def test_baz():
assert True
";
let entries = PythonEntryDetector.detect(src, Path::new("/tmp/project/tests/foo/test_bar.py"));
assert!(
has(&entries, "test_baz", EntryPointKind::Test),
"expected Test entry for `def test_baz` in tests/foo/test_bar.py, got {entries:?}"
);
}
#[test]
fn js_commonjs_object_accumulation_app_use_seeds_as_library_export() {
let src = "var app = exports = module.exports = {};\napp.use = function use(fn) {};\n";
let entries = JsEntryDetector.detect(src, Path::new("/tmp/corpus/lib/application.js"));
assert!(
entries
.iter()
.any(|e| e.kind == EntryPointKind::LibraryExport
&& (e.name == "use" || e.name == "app.use")),
"expected LibraryExport for `app.use` (CommonJS object-accumulation), got {entries:?}"
);
}
#[test]
fn js_module_exports_with_named_props_seeds_all() {
let src = "exports = module.exports = createApplication;\nexports.json = bodyParser.json;\nexports.Router = Router;\n";
let entries = JsEntryDetector.detect(src, Path::new("/tmp/corpus/lib/express.js"));
assert!(
entries
.iter()
.any(|e| e.kind == EntryPointKind::LibraryExport),
"expected at least one LibraryExport entry, got {entries:?}"
);
assert!(
has(&entries, "json", EntryPointKind::LibraryExport),
"expected LibraryExport entry for `exports.json`, got {entries:?}"
);
assert!(
has(&entries, "Router", EntryPointKind::LibraryExport),
"expected LibraryExport entry for `exports.Router`, got {entries:?}"
);
}
#[test]
fn js_real_world_express_application_min() {
let src = "
var app = exports = module.exports = {};
app.init = function init() {};
app.handle = function handle(req, res, callback) {};
app.set = function set(setting, val) {};
app.listen = function listen() {};
app.get = function get(setting) {};
";
let entries = JsEntryDetector.detect(src, Path::new("/tmp/corpus/lib/application.js"));
let lib_exports: Vec<&str> = entries
.iter()
.filter(|e| e.kind == EntryPointKind::LibraryExport)
.map(|e| e.name.as_str())
.collect();
assert!(
lib_exports.len() >= 3,
"expected >=3 LibraryExport entries from app.X = function pattern, got {lib_exports:?}"
);
}
#[test]
fn js_mocha_describe_it_seeds_as_test() {
let src = "describe('foo', function() { it('does X', function() {}); });\n";
let entries = JsEntryDetector.detect(src, Path::new("/tmp/corpus/test/app.test.js"));
assert!(
entries.iter().any(|e| e.kind == EntryPointKind::Test),
"expected Test entry from describe/it call in test file, got {entries:?}"
);
assert!(
has(&entries, "does X", EntryPointKind::Test)
|| has(&entries, "foo", EntryPointKind::Test)
|| has(&entries, "it", EntryPointKind::Test),
"expected Test entry named 'does X' or 'foo' or 'it', got {entries:?}"
);
}
#[test]
fn jvm_java_public_static_void_main_seeds_as_main() {
let src = "public class App {\n public static void main(String[] args) {\n System.out.println(\"hello\");\n }\n}\n";
let entries = JavaEntryDetector.detect(src, Path::new("/tmp/synth/src/App.java"));
assert!(
has(&entries, "main", EntryPointKind::Main),
"expected Main entry for `public static void main(String[] args)`, got {entries:?}"
);
}
#[test]
fn jvm_java_at_test_seeds_as_test() {
let src = "class FooTest {\n @Test\n void testIt() {}\n\n @ParameterizedTest\n void runs(int n) {}\n}\n";
let entries = JavaEntryDetector.detect(src, Path::new("/tmp/synth/src/test/FooTest.java"));
assert!(
has(&entries, "testIt", EntryPointKind::Test),
"expected Test entry for `@Test void testIt`, got {entries:?}"
);
assert!(
has(&entries, "runs", EntryPointKind::Test),
"expected Test entry for `@ParameterizedTest void runs`, got {entries:?}"
);
}
#[test]
fn jvm_java_at_springboot_application_seeds_as_framework_dispatched() {
let src = "@SpringBootApplication\npublic class Application {\n public static void main(String[] args) {}\n}\n";
let entries = JavaEntryDetector.detect(src, Path::new("/tmp/synth/src/Application.java"));
assert!(
has(&entries, "Application", EntryPointKind::FrameworkDispatched),
"expected FrameworkDispatched entry for @SpringBootApplication, got {entries:?}"
);
assert!(
has(&entries, "main", EntryPointKind::Main),
"@SpringBootApplication class's `main` should still seed as Main, got {entries:?}"
);
}
#[test]
fn jvm_java_stereotype_class_annotations_seed_as_library_export() {
let cases = &[
("@Component", "Comp"),
("@Service", "Svc"),
("@Repository", "Repo"),
("@Controller", "Ctrl"),
("@RestController", "Rest"),
("@Configuration", "Cfg"),
];
for (annotation, class_name) in cases {
let src = format!("{annotation}\npublic class {class_name} {{}}\n");
let entries = JavaEntryDetector.detect(&src, Path::new("/tmp/synth/src/X.java"));
assert!(
has(&entries, class_name, EntryPointKind::LibraryExport),
"expected LibraryExport entry for `{annotation} class {class_name}`, got {entries:?}"
);
}
}
#[test]
fn jvm_java_at_bean_method_seeds_as_library_export() {
let src = "@Configuration\npublic class Beans {\n @Bean\n public Foo foo() { return new Foo(); }\n}\n";
let entries = JavaEntryDetector.detect(src, Path::new("/tmp/synth/src/Beans.java"));
assert!(
has(&entries, "foo", EntryPointKind::LibraryExport),
"expected LibraryExport entry for `@Bean public Foo foo()`, got {entries:?}"
);
assert!(
has(&entries, "Beans", EntryPointKind::LibraryExport),
"expected LibraryExport entry for @Configuration class, got {entries:?}"
);
}
#[test]
fn jvm_java_fully_qualified_annotation_resolves_to_trailing_identifier() {
let src = "@org.springframework.stereotype.Component\npublic class Qualified {}\n";
let entries = JavaEntryDetector.detect(src, Path::new("/tmp/synth/src/Qualified.java"));
assert!(
has(&entries, "Qualified", EntryPointKind::LibraryExport),
"expected LibraryExport for fully-qualified @Component, got {entries:?}"
);
}
#[test]
fn jvm_real_world_spring_boot_min_emits_all_three_kinds() {
let src = "
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
@RestController
public class HelloController {
@GetMapping(\"/\")
public String hello() { return \"hi\"; }
}
class HelloTest {
@Test
void greetsTheWorld() {}
}
";
let entries = JavaEntryDetector.detect(src, Path::new("/tmp/synth/src/Mixed.java"));
assert!(
entries
.iter()
.any(|e| e.kind == EntryPointKind::FrameworkDispatched),
"expected at least one FrameworkDispatched entry, got {entries:?}"
);
assert!(
entries
.iter()
.any(|e| e.kind == EntryPointKind::LibraryExport),
"expected at least one LibraryExport entry, got {entries:?}"
);
assert!(
entries.iter().any(|e| e.kind == EntryPointKind::Main),
"expected at least one Main entry, got {entries:?}"
);
assert!(
entries.iter().any(|e| e.kind == EntryPointKind::Test),
"expected at least one Test entry, got {entries:?}"
);
}
#[test]
fn jvm_kotlin_fun_main_seeds_as_main() {
let src = "fun main() {\n println(\"hello\")\n}\n";
let entries = KotlinEntryDetector.detect(src, Path::new("/tmp/synth/src/Main.kt"));
assert!(
has(&entries, "main", EntryPointKind::Main),
"expected Main entry for top-level `fun main()`, got {entries:?}"
);
}
#[test]
fn jvm_kotlin_fun_main_with_args_seeds_as_main() {
let src = "fun main(args: Array<String>) {\n println(args.joinToString())\n}\n";
let entries = KotlinEntryDetector.detect(src, Path::new("/tmp/synth/src/Main.kt"));
assert!(
has(&entries, "main", EntryPointKind::Main),
"expected Main entry for `fun main(args: Array<String>)`, got {entries:?}"
);
}
#[test]
fn jvm_kotlin_class_test_seeds_as_test() {
let src = "class FooTest {\n @Test\n fun bar() {}\n}\n";
let entries = KotlinEntryDetector.detect(src, Path::new("/tmp/synth/src/FooTest.kt"));
assert!(
has(&entries, "FooTest", EntryPointKind::Test),
"expected Test entry for class-named `FooTest`, got {entries:?}"
);
assert!(
has(&entries, "bar", EntryPointKind::Test),
"expected Test entry for `@Test fun bar()`, got {entries:?}"
);
}
#[test]
fn jvm_kotlin_at_component_seeds_as_library_export() {
let src = "@Component\nclass GreetingService {\n fun greet() = \"hi\"\n}\n";
let entries = KotlinEntryDetector.detect(src, Path::new("/tmp/synth/src/GreetingService.kt"));
assert!(
has(&entries, "GreetingService", EntryPointKind::LibraryExport),
"expected LibraryExport entry for @Component class, got {entries:?}"
);
}
#[test]
fn jvm_kotlin_at_springboot_application_seeds_as_framework_dispatched() {
let src = "@SpringBootApplication\nclass DemoApplication\n\nfun main(args: Array<String>) {\n runApplication<DemoApplication>(*args)\n}\n";
let entries = KotlinEntryDetector.detect(src, Path::new("/tmp/synth/src/DemoApplication.kt"));
assert!(
has(
&entries,
"DemoApplication",
EntryPointKind::FrameworkDispatched
),
"expected FrameworkDispatched entry for @SpringBootApplication class, got {entries:?}"
);
assert!(
has(&entries, "main", EntryPointKind::Main),
"Kotlin top-level fun main should still be Main, got {entries:?}"
);
}
#[test]
fn jvm_detector_for_java_and_kotlin_dispatches() {
assert!(
detector_for("java").is_some(),
"detector_for(\"java\") should return JavaEntryDetector"
);
assert!(
detector_for("kt").is_some(),
"detector_for(\"kt\") should return KotlinEntryDetector"
);
assert!(
detector_for("kts").is_some(),
"detector_for(\"kts\") should return KotlinEntryDetector"
);
assert!(
detector_for("kotlin").is_some(),
"detector_for(\"kotlin\") should return KotlinEntryDetector"
);
}