#![cfg(test)]
use assert_cmd::Command;
use predicates::prelude::*;
use std::fs::{self, File};
use tempfile::TempDir;
fn contains_path(content: &str, path: &str) -> bool {
let unix_path = path.replace('\\', "/");
let windows_path = path.replace('/', "\\");
content.contains(&unix_path) || content.contains(&windows_path)
}
mod glob_pattern_integration_tests {
use super::*;
#[test]
fn test_simple_wildcard_patterns() {
let temp_dir = TempDir::new().unwrap();
let root = temp_dir.path();
fs::write(root.join("main.py"), "print('Hello from main')\\n").unwrap();
fs::write(root.join("utils.py"), "def helper(): pass\\n").unwrap();
fs::write(root.join("README.md"), "# Test Project\\n").unwrap();
fs::write(root.join("config.toml"), "[test]\\nvalue = 1\\n").unwrap();
let mut cmd = Command::cargo_bin("context-creator").unwrap();
cmd.current_dir(root)
.arg("--include")
.arg("*.py")
.arg("--output-file")
.arg("output.md");
let output = cmd.output().unwrap();
if !output.status.success() {
panic!(
"Command failed with stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
}
assert!(
root.join("output.md").exists(),
"Output file was not created"
);
let output_content = fs::read_to_string(root.join("output.md")).unwrap();
assert!(contains_path(&output_content, "main.py"));
assert!(contains_path(&output_content, "utils.py"));
assert!(!contains_path(&output_content, "README.md"));
assert!(!contains_path(&output_content, "config.toml"));
}
#[test]
fn test_recursive_directory_matching() {
let temp_dir = TempDir::new().unwrap();
let root = temp_dir.path();
fs::create_dir_all(root.join("src/core")).unwrap();
fs::create_dir_all(root.join("tests")).unwrap();
File::create(root.join("main.rs")).unwrap();
File::create(root.join("src/lib.rs")).unwrap();
File::create(root.join("src/core/mod.rs")).unwrap();
File::create(root.join("tests/test.rs")).unwrap();
File::create(root.join("README.md")).unwrap();
let mut cmd = Command::cargo_bin("context-creator").unwrap();
cmd.current_dir(root)
.arg("--include")
.arg("**/*.rs")
.arg("--output-file")
.arg("output.md");
cmd.assert().success();
let output_content = fs::read_to_string(root.join("output.md")).unwrap();
assert!(contains_path(&output_content, "main.rs"));
assert!(contains_path(&output_content, "src/lib.rs"));
assert!(contains_path(&output_content, "src/core/mod.rs"));
assert!(contains_path(&output_content, "tests/test.rs"));
assert!(!contains_path(&output_content, "README.md"));
}
#[test]
fn test_brace_expansion() {
let temp_dir = TempDir::new().unwrap();
let root = temp_dir.path();
fs::create_dir_all(root.join("src/api")).unwrap();
fs::create_dir_all(root.join("tests")).unwrap();
File::create(root.join("src/main.py")).unwrap();
File::create(root.join("src/app.js")).unwrap();
File::create(root.join("src/api/handler.py")).unwrap();
File::create(root.join("src/api/client.js")).unwrap();
File::create(root.join("tests/test.py")).unwrap(); File::create(root.join("src/config.toml")).unwrap();
let mut cmd = Command::cargo_bin("context-creator").unwrap();
cmd.current_dir(root)
.arg("--include")
.arg("src/**/*.{py,js}")
.arg("--output-file")
.arg("output.md");
cmd.assert().success();
let output_content = fs::read_to_string(root.join("output.md")).unwrap();
assert!(contains_path(&output_content, "src/main.py"));
assert!(contains_path(&output_content, "src/app.js"));
assert!(contains_path(&output_content, "src/api/handler.py"));
assert!(contains_path(&output_content, "src/api/client.js"));
assert!(!contains_path(&output_content, "tests/test.py"));
assert!(!contains_path(&output_content, "src/config.toml"));
}
#[test]
fn test_character_sets_and_ranges() {
let temp_dir = TempDir::new().unwrap();
let root = temp_dir.path();
fs::create_dir_all(root.join("tests")).unwrap();
File::create(root.join("test1.py")).unwrap();
File::create(root.join("test2.py")).unwrap();
File::create(root.join("test9.py")).unwrap();
File::create(root.join("tests/test3.py")).unwrap();
File::create(root.join("test.py")).unwrap(); File::create(root.join("test10.py")).unwrap(); File::create(root.join("testA.py")).unwrap();
let mut cmd = Command::cargo_bin("context-creator").unwrap();
cmd.current_dir(root)
.arg("--include")
.arg("**/test[0-9].py")
.arg("--output-file")
.arg("output.md");
cmd.assert().success();
let output_content = fs::read_to_string(root.join("output.md")).unwrap();
assert!(contains_path(&output_content, "test1.py"));
assert!(contains_path(&output_content, "test2.py"));
assert!(contains_path(&output_content, "test9.py"));
assert!(contains_path(&output_content, "tests/test3.py"));
assert!(!contains_path(&output_content, "test.py"));
assert!(!contains_path(&output_content, "test10.py"));
assert!(!contains_path(&output_content, "testA.py"));
}
#[test]
fn test_complex_pattern_combinations() {
let temp_dir = TempDir::new().unwrap();
let root = temp_dir.path();
fs::create_dir_all(root.join("src/db/models")).unwrap();
fs::create_dir_all(root.join("src/api")).unwrap();
fs::create_dir_all(root.join("db/migrations")).unwrap();
File::create(root.join("src/user_repository.py")).unwrap();
File::create(root.join("src/api/auth_service.py")).unwrap();
File::create(root.join("src/user_model.py")).unwrap();
File::create(root.join("src/db/models/base.py")).unwrap();
File::create(root.join("src/db/connection.py")).unwrap();
File::create(root.join("db/migrations/001.sql")).unwrap();
File::create(root.join("src/utils.py")).unwrap(); File::create(root.join("src/config.py")).unwrap();
let mut cmd = Command::cargo_bin("context-creator").unwrap();
cmd.current_dir(root)
.arg("--include")
.arg("**/*{repository,service,model}*.py")
.arg("--include")
.arg("**/db/**")
.arg("--output-file")
.arg("output.md");
cmd.assert().success();
let output_content = fs::read_to_string(root.join("output.md")).unwrap();
assert!(contains_path(&output_content, "src/user_repository.py"));
assert!(contains_path(&output_content, "src/api/auth_service.py"));
assert!(contains_path(&output_content, "src/user_model.py"));
assert!(contains_path(&output_content, "src/db/models/base.py"));
assert!(contains_path(&output_content, "src/db/connection.py"));
assert!(contains_path(&output_content, "db/migrations/001.sql"));
assert!(!contains_path(&output_content, "src/utils.py"));
assert!(!contains_path(&output_content, "src/config.py"));
}
#[test]
fn test_invalid_glob_pattern_error() {
let temp_dir = TempDir::new().unwrap();
let root = temp_dir.path();
let mut cmd = Command::cargo_bin("context-creator").unwrap();
cmd.current_dir(root)
.arg("--include")
.arg("src/[") .arg("--output-file")
.arg("output.md");
cmd.assert()
.failure()
.stderr(predicate::str::contains("Invalid include pattern"));
}
#[test]
fn test_empty_pattern_handling() {
let temp_dir = TempDir::new().unwrap();
let root = temp_dir.path();
File::create(root.join("test.py")).unwrap();
let mut cmd = Command::cargo_bin("context-creator").unwrap();
cmd.current_dir(root)
.arg("--include")
.arg("") .arg("--include")
.arg("*.py") .arg("--output-file")
.arg("output.md");
cmd.assert().success();
let output_content = fs::read_to_string(root.join("output.md")).unwrap();
assert!(contains_path(&output_content, "test.py"));
}
#[test]
fn test_multiple_include_patterns() {
let temp_dir = TempDir::new().unwrap();
let root = temp_dir.path();
File::create(root.join("main.py")).unwrap();
File::create(root.join("app.js")).unwrap();
File::create(root.join("config.rs")).unwrap();
File::create(root.join("README.md")).unwrap();
let mut cmd = Command::cargo_bin("context-creator").unwrap();
cmd.current_dir(root)
.arg("--include")
.arg("*.py")
.arg("--include")
.arg("*.js")
.arg("--output-file")
.arg("output.md");
cmd.assert().success();
let output_content = fs::read_to_string(root.join("output.md")).unwrap();
assert!(contains_path(&output_content, "main.py"));
assert!(contains_path(&output_content, "app.js"));
assert!(!contains_path(&output_content, "config.rs"));
assert!(!contains_path(&output_content, "README.md"));
}
#[test]
fn test_quoted_patterns_prevent_shell_expansion() {
let temp_dir = TempDir::new().unwrap();
let root = temp_dir.path();
fs::create_dir_all(root.join("src")).unwrap();
File::create(root.join("src/main.rs")).unwrap();
File::create(root.join("src/lib.rs")).unwrap();
File::create(root.join("test.py")).unwrap();
let mut cmd = Command::cargo_bin("context-creator").unwrap();
cmd.current_dir(root)
.arg("--include")
.arg("src/*.rs") .arg("--output-file")
.arg("output.md");
cmd.assert().success();
let output_content = fs::read_to_string(root.join("output.md")).unwrap();
assert!(contains_path(&output_content, "src/main.rs"));
assert!(contains_path(&output_content, "src/lib.rs"));
assert!(!contains_path(&output_content, "test.py"));
}
}
mod edge_case_tests {
use super::*;
#[test]
fn test_pattern_with_no_matches() {
let temp_dir = TempDir::new().unwrap();
let root = temp_dir.path();
File::create(root.join("test.py")).unwrap();
File::create(root.join("README.md")).unwrap();
let mut cmd = Command::cargo_bin("context-creator").unwrap();
cmd.current_dir(root)
.arg("--include")
.arg("*.rs") .arg("--output-file")
.arg("output.md");
cmd.assert().success();
let output_content = fs::read_to_string(root.join("output.md")).unwrap();
assert!(output_content.contains("Total files: 0"));
}
#[test]
fn test_include_patterns_with_positional_args_now_allowed() {
let temp_dir = TempDir::new().unwrap();
let root = temp_dir.path();
fs::create_dir_all(root.join("src")).unwrap();
File::create(root.join("src/test.py")).unwrap();
File::create(root.join("src/main.rs")).unwrap();
File::create(root.join("other.py")).unwrap();
let mut cmd = Command::cargo_bin("context-creator").unwrap();
cmd.current_dir(root)
.arg("src/") .arg("--include")
.arg("*.py")
.arg("--output-file")
.arg("output.md");
cmd.assert().success();
let output_content = fs::read_to_string(root.join("output.md")).unwrap();
assert!(contains_path(&output_content, "test.py")); }
#[test]
fn test_help_shows_glob_examples() {
let mut cmd = Command::cargo_bin("context-creator").unwrap();
cmd.arg("--help");
cmd.assert()
.success()
.stdout(predicate::str::contains("glob pattern"))
.stdout(predicate::str::contains("quote patterns"))
.stdout(predicate::str::contains(
"For usage examples, run: context-creator examples",
));
let mut cmd = Command::cargo_bin("context-creator").unwrap();
cmd.arg("examples");
cmd.assert()
.success()
.stdout(predicate::str::contains("**/*.py"))
.stdout(predicate::str::contains("Pattern Matching:"))
.stdout(predicate::str::contains("--include \"**/*.py\""));
}
}