use leankg::indexer::{
AndroidHiltExtractor, AndroidManifestExtractor, AndroidResourceRefExtractor,
AndroidRoomExtractor,
};
use std::fs;
const TV_APP_DIR: &str = "tests/fixtures/complex_scenarios/tv_app";
#[test]
fn test_tv_app_full_extraction() {
let manifest_path = format!("{}/AndroidManifest.xml", TV_APP_DIR);
let manifest_source = fs::read_to_string(&manifest_path).expect("TV app manifest not found");
let manifest_extractor =
AndroidManifestExtractor::new(manifest_source.as_bytes(), &manifest_path);
let (manifest_elements, manifest_rels) = manifest_extractor.extract();
let activities: Vec<_> = manifest_elements
.iter()
.filter(|e| e.element_type == "android_activity")
.collect();
assert!(!activities.is_empty(), "TV app should have activities");
let services: Vec<_> = manifest_elements
.iter()
.filter(|e| e.element_type == "android_service")
.collect();
println!("Found {} services", services.len());
let app_rels: Vec<_> = manifest_rels
.iter()
.filter(|r| r.rel_type == "has_application_class")
.collect();
println!("Found {} app class relationships", app_rels.len());
}
#[test]
fn test_room_entities_with_relationships() {
let entity_path = format!(
"{}/src/main/java/com/tv/app/data/local/entity/ChannelEntity.kt",
TV_APP_DIR
);
let entity_source = fs::read_to_string(&entity_path).expect("Entity file not found");
let room_extractor = AndroidRoomExtractor::new(entity_source.as_bytes(), &entity_path);
let (elements, relationships) = room_extractor.extract();
let entities: Vec<_> = elements
.iter()
.filter(|e| e.element_type == "room_entity")
.collect();
assert!(!entities.is_empty(), "Should extract Room entities");
let fk_rels: Vec<_> = relationships
.iter()
.filter(|r| r.rel_type == "room_entity_has_foreign_key")
.collect();
assert!(
!fk_rels.is_empty() || entities.len() >= 2,
"Should have entities with FK relationships or multiple entities"
);
}
#[test]
fn test_dao_with_queries() {
let dao_path = format!(
"{}/src/main/java/com/tv/app/data/local/dao/ChannelDao.kt",
TV_APP_DIR
);
let dao_source = fs::read_to_string(&dao_path).expect("DAO file not found");
let room_extractor = AndroidRoomExtractor::new(dao_source.as_bytes(), &dao_path);
let (elements, relationships) = room_extractor.extract();
let daos: Vec<_> = elements
.iter()
.filter(|e| e.element_type == "room_dao")
.collect();
assert!(!daos.is_empty(), "Should extract DAOs");
let query_rels: Vec<_> = relationships
.iter()
.filter(|r| r.rel_type == "room_dao_queries_entity")
.collect();
assert!(
!query_rels.is_empty() || daos.len() > 0,
"Should have DAO query relationships or DAOs present"
);
}
#[test]
fn test_hilt_module_providers() {
let module_path = format!("{}/src/main/java/com/tv/app/di/AppModule.kt", TV_APP_DIR);
let module_source = fs::read_to_string(&module_path).expect("Hilt module file not found");
let hilt_extractor = AndroidHiltExtractor::new(module_source.as_bytes(), &module_path);
let (elements, relationships) = hilt_extractor.extract();
let modules: Vec<_> = elements
.iter()
.filter(|e| e.element_type == "hilt_module")
.collect();
println!(
"Found {} Hilt modules: {:?}",
modules.len(),
modules.iter().map(|e| &e.name).collect::<Vec<_>>()
);
let providers: Vec<_> = elements
.iter()
.filter(|e| e.element_type == "hilt_provider")
.collect();
println!("Found {} Hilt providers", providers.len());
assert!(
modules.len() > 0 || providers.len() > 0 || relationships.len() > 0,
"Should extract something from Hilt module"
);
}
#[test]
fn test_repository_with_inject() {
let repo_path = format!(
"{}/src/main/java/com/tv/app/data/repository/ChannelRepository.kt",
TV_APP_DIR
);
let repo_source = fs::read_to_string(&repo_path).expect("Repository file not found");
let hilt_extractor = AndroidHiltExtractor::new(repo_source.as_bytes(), &repo_path);
let (_, relationships) = hilt_extractor.extract();
let inject_rels: Vec<_> = relationships
.iter()
.filter(|r| r.rel_type.contains("injected"))
.collect();
assert!(
repo_source.contains("ChannelRepository"),
"Repository should contain class name"
);
assert!(
repo_source.contains("@Inject") || repo_source.contains("constructor("),
"Repository should have injection pattern"
);
}
#[test]
fn test_tv_app_structure_completeness() {
let expected_files = vec![
format!("{}/AndroidManifest.xml", TV_APP_DIR),
format!("{}/src/main/java/com/tv/app/TvApplication.kt", TV_APP_DIR),
format!("{}/src/main/java/com/tv/app/di/AppModule.kt", TV_APP_DIR),
format!(
"{}/src/main/java/com/tv/app/data/local/TvDatabase.kt",
TV_APP_DIR
),
format!(
"{}/src/main/java/com/tv/app/data/local/entity/ChannelEntity.kt",
TV_APP_DIR
),
format!(
"{}/src/main/java/com/tv/app/data/local/dao/ChannelDao.kt",
TV_APP_DIR
),
format!(
"{}/src/main/java/com/tv/app/data/remote/PlaylistApi.kt",
TV_APP_DIR
),
format!(
"{}/src/main/java/com/tv/app/data/repository/ChannelRepository.kt",
TV_APP_DIR
),
format!(
"{}/src/main/java/com/tv/app/ui/browse/BrowseFragment.kt",
TV_APP_DIR
),
format!(
"{}/src/main/java/com/tv/app/ui/player/PlayerActivity.kt",
TV_APP_DIR
),
];
for path in &expected_files {
assert!(
fs::metadata(path).is_ok(),
"Expected TV app file should exist: {}",
path
);
}
}
#[test]
fn test_cross_file_relationships() {
let db_path = format!(
"{}/src/main/java/com/tv/app/data/local/TvDatabase.kt",
TV_APP_DIR
);
let db_source = fs::read_to_string(&db_path).expect("Database file not found");
let room_extractor = AndroidRoomExtractor::new(db_source.as_bytes(), &db_path);
let (elements, relationships) = room_extractor.extract();
let databases: Vec<_> = elements
.iter()
.filter(|e| e.element_type == "room_database")
.collect();
assert!(!databases.is_empty(), "Should extract Room database");
let db_entity_rels: Vec<_> = relationships
.iter()
.filter(|r| r.rel_type == "room_database_contains_entity")
.collect();
assert_eq!(databases.len(), 1, "Should have one database");
}
#[test]
fn test_resource_references_in_ui() {
let fragment_path = format!(
"{}/src/main/java/com/tv/app/ui/browse/BrowseFragment.kt",
TV_APP_DIR
);
let fragment_source = fs::read_to_string(&fragment_path).expect("Fragment file not found");
assert!(
fragment_source.contains("@AndroidEntryPoint")
|| fragment_source.contains("class BrowseFragment"),
"Should be Hilt-enabled fragment or have BrowseFragment class"
);
let res_extractor =
AndroidResourceRefExtractor::new(fragment_source.as_bytes(), &fragment_path);
let (_, relationships) = res_extractor.extract();
for rel in &relationships {
assert!(!rel.rel_type.is_empty(), "Relationship should have type");
}
}