//! Integration tests for signal-gateway-code-tool. //! //! These tests exercise the GitHub tarball download and file browsing functionality //! against a real public repository at a pinned commit. use signal_gateway_code_tool::{CodeTool, CodeToolConfig, GitHubRepo, ShaCallback, Source}; use std::sync::Arc; /// Test against cbeck88/ver-stub-rs at a known commit. /// This is a public repo, so no token is needed. const TEST_OWNER: &str = "cbeck88"; const TEST_REPO: &str = "ver-stub-rs"; const TEST_SHA: &str = "79b98e25f27ae4f5dd73a5a3d8f37dad655a57e8"; fn create_test_repo_code() -> CodeTool { create_test_repo_code_with_glob(vec![]) } fn create_test_repo_code_with_glob(glob: Vec) -> CodeTool { let config = CodeToolConfig { name: "test-app".to_string(), source: Source::GitHub { repo: GitHubRepo { owner: TEST_OWNER.to_string(), repo: TEST_REPO.to_string(), }, token_file: None, // Public repo, no auth needed }, glob, include_non_utf8: false, summary_file: None, }; let sha = TEST_SHA.to_string(); let sha_callback: ShaCallback = Arc::new(move || { let sha = sha.clone(); Box::pin(async move { Ok(sha) }) }); CodeTool::new(config, sha_callback).expect("Failed to create CodeTool") } #[tokio::test] async fn test_ls_root() { let app = create_test_repo_code(); let result = app.ls(None).await.expect("ls failed"); // Verify expected top-level entries exist assert!(result.contains("Cargo.toml"), "Should contain Cargo.toml"); assert!(result.contains("README.md"), "Should contain README.md"); assert!( result.contains("ver-stub/"), "Should contain ver-stub/ directory" ); assert!( result.contains("ver-stub-build/"), "Should contain ver-stub-build/ directory" ); assert!(result.contains("tests.sh"), "Should contain tests.sh"); } #[tokio::test] async fn test_ls_subdirectory() { let app = create_test_repo_code(); let result = app.ls(Some("ver-stub")).await.expect("ls failed"); // Should have src/ directory and Cargo.toml assert!(result.contains("src/"), "Should contain src/ directory"); assert!(result.contains("Cargo.toml"), "Should contain Cargo.toml"); } #[tokio::test] async fn test_find_rust_files() { let app = create_test_repo_code(); let result = app.find(Some("*.rs")).await.expect("find failed"); // Should find Rust source files assert!( result.contains("ver-stub/src/lib.rs"), "Should find ver-stub/src/lib.rs" ); assert!( result.contains("ver-stub-build/src/lib.rs"), "Should find ver-stub-build/src/lib.rs" ); } #[tokio::test] async fn test_find_with_path_pattern() { let app = create_test_repo_code(); let result = app .find(Some("ver-stub-build/src/*.rs")) .await .expect("find failed"); // Should find files in ver-stub-build/src/ assert!( result.contains("ver-stub-build/src/lib.rs"), "Should find lib.rs" ); } #[tokio::test] async fn test_read_cargo_toml() { let app = create_test_repo_code(); let result = app .read("Cargo.toml", None, None) .await .expect("read failed"); // Verify content matches what we know is in the file assert!(result.contains("[workspace]"), "Should contain [workspace]"); assert!(result.contains("ver-stub"), "Should contain ver-stub"); assert!( result.contains("ver-stub-build"), "Should contain ver-stub-build" ); } #[tokio::test] async fn test_read_with_line_range() { let app = create_test_repo_code(); // Read just the first 5 lines let result = app .read("Cargo.toml", Some(1), Some(5)) .await .expect("read failed"); // Should only have 5 lines let line_count = result.lines().count(); assert_eq!(line_count, 5, "Should have exactly 5 lines"); // First line should be [workspace] assert!( result.contains("[workspace]"), "First lines should contain [workspace]" ); } #[tokio::test] async fn test_read_nonexistent_file() { let app = create_test_repo_code(); let result = app.read("nonexistent-file.txt", None, None).await; assert!(result.is_err(), "Should fail for nonexistent file"); assert!( result.unwrap_err().contains("not found"), "Error should mention file not found" ); } #[tokio::test] async fn test_search_simple() { let app = create_test_repo_code(); let result = app .search("workspace", 0, None) .await .expect("search failed"); // Should find "workspace" in Cargo.toml assert!( result.contains("Cargo.toml"), "Should find match in Cargo.toml" ); } #[tokio::test] async fn test_search_with_context() { let app = create_test_repo_code(); let result = app .search("resolver", 2, None) .await .expect("search failed"); // Should have context lines around the match assert!( result.contains("Cargo.toml"), "Should find match in Cargo.toml" ); // With context, should see surrounding lines assert!( result.contains("[workspace]"), "Should show context including [workspace]" ); } #[tokio::test] async fn test_search_with_path_prefix() { let app = create_test_repo_code(); // Search only in ver-stub-build directory let result = app .search("pub", 0, Some("ver-stub-build/src")) .await .expect("search failed"); // Should only find matches in ver-stub-build/src for line in result.lines() { if line.contains(":") && !line.starts_with('[') { // This is a match line (path:line: content) assert!( line.starts_with("ver-stub-build/src"), "All matches should be in ver-stub-build/src, got: {}", line ); } } } #[tokio::test] async fn test_search_no_matches() { let app = create_test_repo_code(); let result = app .search("xyzzy_unlikely_string_12345", 0, None) .await .expect("search failed"); assert!(result.contains("No matches"), "Should report no matches"); } #[tokio::test] async fn test_glob_filter_rust_files_only() { // Only include .rs files let app = create_test_repo_code_with_glob(vec!["**/*.rs".to_string()]); let result = app.find(Some("*")).await.expect("find failed"); // Should find Rust files assert!(result.contains(".rs"), "Should contain .rs files"); // Should NOT find non-Rust files assert!( !result.contains("Cargo.toml"), "Should not contain Cargo.toml" ); assert!( !result.contains("README.md"), "Should not contain README.md" ); assert!(!result.contains("tests.sh"), "Should not contain tests.sh"); } #[tokio::test] async fn test_glob_filter_specific_directory() { // Only include files in ver-stub/src let app = create_test_repo_code_with_glob(vec!["ver-stub/src/**".to_string()]); let result = app.find(Some("*")).await.expect("find failed"); // Should find files in ver-stub/src assert!( result.contains("ver-stub/src/lib.rs"), "Should contain ver-stub/src/lib.rs" ); // Should NOT find files outside ver-stub/src assert!( !result.contains("ver-stub-build/"), "Should not contain ver-stub-build files" ); assert!( !result.contains("Cargo.toml"), "Should not contain root Cargo.toml" ); } #[tokio::test] async fn test_glob_filter_multiple_patterns() { // Include both Cargo.toml files and shell scripts let app = create_test_repo_code_with_glob(vec!["**/Cargo.toml".to_string(), "*.sh".to_string()]); let result = app.find(Some("*")).await.expect("find failed"); // Should find Cargo.toml files assert!(result.contains("Cargo.toml"), "Should contain Cargo.toml"); // Should find shell scripts assert!(result.contains("tests.sh"), "Should contain tests.sh"); // Should NOT find other files assert!( !result.contains("README.md"), "Should not contain README.md" ); assert!(!result.contains(".rs"), "Should not contain .rs files"); } #[tokio::test] async fn test_glob_filter_ls_shows_filtered_dirs() { // Only include files in ver-stub directory let app = create_test_repo_code_with_glob(vec!["ver-stub/**".to_string()]); let result = app.ls(None).await.expect("ls failed"); // Root ls should only show ver-stub/ since other dirs are empty after filtering assert!(result.contains("ver-stub/"), "Should show ver-stub/"); // Other directories should not appear (they have no matching files) assert!( !result.contains("ver-stub-build/"), "Should not show ver-stub-build/" ); }