#[cfg(test)]
mod ast_vs_regex {
use crate::exports::ast;
use crate::exports::{python, rust_lang, typescript};
#[test]
fn ts_basic_parity() {
let src = r#"
export function createAuth(config: Config): Auth {}
export class AuthService {}
export interface AuthConfig {}
export type TokenType = string;
export const DEFAULT_TTL = 3600;
export enum AuthStatus { Active, Expired }
"#;
let regex_result = typescript::extract_exports(src);
let ast_result = ast::typescript::extract_exports(src);
assert_eq!(ast_result, regex_result);
}
#[test]
fn ts_re_exports_with_alias() {
let src = r#"
export { Foo, Bar as Baz } from './module';
"#;
let regex_result = typescript::extract_exports(src);
let ast_result = ast::typescript::extract_exports(src);
assert!(ast_result.contains(&"Foo".to_string()));
assert!(ast_result.contains(&"Baz".to_string()));
assert_eq!(ast_result, regex_result);
}
#[test]
fn ts_comments_ignored() {
let src = r#"
// export function notExported() {}
/* export class AlsoNot {} */
export function realExport(): void {}
"#;
let regex_result = typescript::extract_exports(src);
let ast_result = ast::typescript::extract_exports(src);
assert_eq!(ast_result, vec!["realExport"]);
assert_eq!(ast_result, regex_result);
}
#[test]
fn ts_default_export_class() {
let src = r#"
export default class MyApp {}
"#;
let regex_result = typescript::extract_exports(src);
let ast_result = ast::typescript::extract_exports(src);
assert!(ast_result.contains(&"MyApp".to_string()));
assert_eq!(ast_result, regex_result);
}
#[test]
fn ts_async_abstract() {
let src = r#"
export async function fetchData() {}
export abstract class BaseService {}
"#;
let regex_result = typescript::extract_exports(src);
let ast_result = ast::typescript::extract_exports(src);
assert!(ast_result.contains(&"fetchData".to_string()));
assert!(ast_result.contains(&"BaseService".to_string()));
assert_eq!(ast_result, regex_result);
}
#[test]
fn ts_wildcard_namespace() {
let src = r#"
export * as Utils from './utils';
export * as Types from './types';
"#;
let regex_result = typescript::extract_exports(src);
let ast_result = ast::typescript::extract_exports(src);
assert_eq!(ast_result, vec!["Utils", "Types"]);
assert_eq!(ast_result, regex_result);
}
#[test]
fn ts_wildcard_with_resolver() {
let src = r#"
export * from './helpers';
export function main() {}
"#;
let helper = r#"
export function helperA() {}
export function helperB() {}
export const HELPER_CONST = 42;
"#;
let resolver = |path: &str| -> Option<String> {
if path == "./helpers" {
Some(helper.to_string())
} else {
None
}
};
let regex_result = typescript::extract_exports_with_resolver(src, Some(&resolver));
let ast_result = ast::typescript::extract_exports_with_resolver(src, Some(&resolver));
assert!(ast_result.contains(&"main".to_string()));
assert!(ast_result.contains(&"helperA".to_string()));
assert!(ast_result.contains(&"helperB".to_string()));
assert!(ast_result.contains(&"HELPER_CONST".to_string()));
let mut ast_sorted = ast_result.clone();
let mut regex_sorted = regex_result.clone();
ast_sorted.sort();
regex_sorted.sort();
assert_eq!(ast_sorted, regex_sorted);
}
#[test]
fn ts_export_inside_string_literal() {
let src = r#"
export function real() {}
const template = `
export function fake() {}
`;
"#;
let ast_result = ast::typescript::extract_exports(src);
assert_eq!(ast_result, vec!["real"]);
}
#[test]
fn ts_conditional_export() {
let src = r#"
if (process.env.NODE_ENV === 'development') {
export function debugHelper() {}
}
export function main() {}
"#;
let ast_result = ast::typescript::extract_exports(src);
assert!(ast_result.contains(&"main".to_string()));
assert!(ast_result.contains(&"debugHelper".to_string()));
}
#[test]
fn py_basic_parity() {
let src = r#"
def create_auth(config):
pass
class AuthService:
pass
def _internal():
pass
"#;
let regex_result = python::extract_exports(src);
let ast_result = ast::python::extract_exports(src);
assert_eq!(ast_result, regex_result);
}
#[test]
fn py_all_takes_precedence() {
let src = r#"
__all__ = ["create_auth", "AuthService"]
def create_auth(config):
pass
class AuthService:
pass
def extra_func():
pass
"#;
let regex_result = python::extract_exports(src);
let ast_result = ast::python::extract_exports(src);
assert_eq!(ast_result, vec!["create_auth", "AuthService"]);
assert_eq!(ast_result, regex_result);
}
#[test]
fn py_nested_not_captured() {
let src = r#"
class Outer:
class Inner:
pass
def method(self):
pass
def top_level():
def nested():
pass
"#;
let regex_result = python::extract_exports(src);
let ast_result = ast::python::extract_exports(src);
assert_eq!(ast_result, regex_result);
assert!(ast_result.contains(&"Outer".to_string()));
assert!(!ast_result.contains(&"Inner".to_string()));
}
#[test]
fn py_decorated_functions() {
let src = r#"
@dataclass
class Config:
host: str
@staticmethod
def create():
pass
"#;
let regex_result = python::extract_exports(src);
let ast_result = ast::python::extract_exports(src);
assert!(ast_result.contains(&"Config".to_string()));
assert!(ast_result.contains(&"create".to_string()));
assert_eq!(ast_result, regex_result);
}
#[test]
fn rs_basic_parity() {
let src = r#"
pub fn create_auth(config: Config) -> Auth {}
pub struct AuthService {}
pub enum AuthStatus { Active, Expired }
pub trait Authenticator {}
pub type Token = String;
pub const DEFAULT_TTL: u64 = 3600;
pub static INSTANCE: Lazy<Auth> = Lazy::new(|| Auth::new());
fn private_fn() {}
struct PrivateStruct {}
"#;
let regex_result = rust_lang::extract_exports(src);
let ast_result = ast::rust_lang::extract_exports(src);
assert_eq!(ast_result, regex_result);
}
#[test]
fn rs_pub_crate() {
let src = r#"
pub(crate) fn internal_fn() {}
pub(crate) struct InternalStruct {}
"#;
let regex_result = rust_lang::extract_exports(src);
let ast_result = ast::rust_lang::extract_exports(src);
assert_eq!(ast_result, regex_result);
}
#[test]
fn rs_async_unsafe() {
let src = r#"
pub async fn async_fn() {}
pub unsafe fn unsafe_fn() {}
"#;
let regex_result = rust_lang::extract_exports(src);
let ast_result = ast::rust_lang::extract_exports(src);
assert_eq!(ast_result, regex_result);
}
#[test]
fn rs_pub_in_string_literal() {
let src = "pub fn real_fn() {}\nfn other() { let s = \"pub fn fake() {}\"; }\n";
let ast_result = ast::rust_lang::extract_exports(src);
assert_eq!(ast_result, vec!["real_fn"]);
}
#[test]
fn rs_feature_gated() {
let src = r#"
#[cfg(feature = "optional")]
pub fn optional_fn() {}
pub fn always_fn() {}
"#;
let regex_result = rust_lang::extract_exports(src);
let ast_result = ast::rust_lang::extract_exports(src);
assert_eq!(ast_result, regex_result);
}
#[test]
fn rs_pub_mod() {
let src = r#"
pub mod submodule;
mod private_mod;
"#;
let regex_result = rust_lang::extract_exports(src);
let ast_result = ast::rust_lang::extract_exports(src);
assert_eq!(ast_result, regex_result);
}
}