#![cfg(test)]
use clap::Parser;
use context_creator::cli::Config;
use std::path::PathBuf;
use tempfile::TempDir;
#[test]
fn test_prompt_with_multiple_paths() {
let temp_dir = TempDir::new().unwrap();
let auth_dir = temp_dir.path().join("auth");
let security_dir = temp_dir.path().join("security");
let core_dir = temp_dir.path().join("core");
let integration_dir = temp_dir.path().join("integration");
std::fs::create_dir(&auth_dir).unwrap();
std::fs::create_dir(&security_dir).unwrap();
std::fs::create_dir(&core_dir).unwrap();
std::fs::create_dir(&integration_dir).unwrap();
let config = Config::parse_from([
"context-creator",
"--prompt",
"Analyze multiple modules",
auth_dir.to_str().unwrap(),
security_dir.to_str().unwrap(),
core_dir.to_str().unwrap(),
integration_dir.to_str().unwrap(),
]);
assert_eq!(
config.get_prompt(),
Some("Analyze multiple modules".to_string())
);
assert_eq!(
config.paths,
Some(vec![auth_dir, security_dir, core_dir, integration_dir])
);
assert!(config.validate().is_ok());
}
#[test]
fn test_stdin_with_multiple_paths_and_patterns() {
let temp_dir = TempDir::new().unwrap();
let src_dir = temp_dir.path().join("src");
let docs_dir = temp_dir.path().join("docs");
std::fs::create_dir(&src_dir).unwrap();
std::fs::create_dir(&docs_dir).unwrap();
let config = Config::parse_from([
"context-creator",
"--stdin",
"--include",
"**/*.rs",
"--include",
"**/*.toml",
"--ignore",
"target/**",
"--ignore",
"**/*_test.rs",
src_dir.to_str().unwrap(),
docs_dir.to_str().unwrap(),
]);
assert!(config.read_stdin);
assert_eq!(config.get_include_patterns(), vec!["**/*.rs", "**/*.toml"]);
assert_eq!(
config.get_ignore_patterns(),
vec!["target/**", "**/*_test.rs"]
);
assert_eq!(config.paths, Some(vec![src_dir, docs_dir]));
assert!(config.validate().is_ok());
}
#[test]
fn test_prompt_with_repo_and_options() {
let config = Config::parse_from([
"context-creator",
"--prompt",
"Security audit of external repo",
"--remote",
"https://github.com/owner/repo",
"--max-tokens",
"500000",
"--verbose",
"--progress",
]);
assert_eq!(
config.get_prompt(),
Some("Security audit of external repo".to_string())
);
assert_eq!(
config.remote,
Some("https://github.com/owner/repo".to_string())
);
assert_eq!(config.max_tokens, Some(500000));
assert_eq!(config.verbose, 1);
assert!(config.progress);
assert!(config.validate().is_ok());
}
#[test]
fn test_complex_include_exclude_patterns() {
let config = Config::parse_from([
"context-creator",
"--prompt",
"Find specific patterns",
"--include",
"src/**/*.{rs,py,js}",
"--include",
"tests/**/test_*.rs",
"--include",
"docs/**/*.md",
"--ignore",
"target/**",
"--ignore",
"node_modules/**",
"--ignore",
"**/*.pyc",
"--ignore",
".git/**",
]);
assert_eq!(
config.get_prompt(),
Some("Find specific patterns".to_string())
);
assert_eq!(
config.get_include_patterns(),
vec!["src/**/*.{rs,py,js}", "tests/**/test_*.rs", "docs/**/*.md"]
);
assert_eq!(
config.get_ignore_patterns(),
vec!["target/**", "node_modules/**", "**/*.pyc", ".git/**"]
);
assert!(config.validate().is_ok());
}
#[test]
fn test_all_semantic_options_with_prompt() {
let config = Config::parse_from([
"context-creator",
"--prompt",
"Deep semantic analysis",
"--include",
"src/**/*.rs",
"--trace-imports",
"--include-callers",
"--include-types",
"--semantic-depth",
"5",
"--enhanced-context",
]);
assert_eq!(
config.get_prompt(),
Some("Deep semantic analysis".to_string())
);
assert_eq!(config.get_include_patterns(), vec!["src/**/*.rs"]);
assert!(config.trace_imports);
assert!(config.include_callers);
assert!(config.include_types);
assert_eq!(config.semantic_depth, 5);
assert!(config.enhanced_context);
assert!(config.validate().is_ok());
}
#[test]
fn test_stdin_with_all_options() {
let temp_dir = TempDir::new().unwrap();
let src_dir = temp_dir.path().join("src");
std::fs::create_dir(&src_dir).unwrap();
let config = Config::parse_from([
"context-creator",
"--stdin",
"--include",
"**/*.rs",
"--ignore",
"target/**",
"--max-tokens",
"1000000",
"--tool",
"codex",
"--verbose",
"--progress",
"--enhanced-context",
"--trace-imports",
"--semantic-depth",
"3",
src_dir.to_str().unwrap(),
]);
assert!(config.read_stdin);
assert_eq!(config.get_include_patterns(), vec!["**/*.rs"]);
assert_eq!(config.get_ignore_patterns(), vec!["target/**"]);
assert_eq!(config.max_tokens, Some(1000000));
assert_eq!(config.llm_tool.command(), "codex");
assert_eq!(config.verbose, 1);
assert!(config.progress);
assert!(config.enhanced_context);
assert!(config.trace_imports);
assert_eq!(config.semantic_depth, 3);
assert_eq!(config.paths, Some(vec![src_dir]));
assert!(config.validate().is_ok());
}
#[test]
fn test_whitespace_prompt_handling() {
let temp_dir = TempDir::new().unwrap();
let src_dir = temp_dir.path().join("src");
std::fs::create_dir(&src_dir).unwrap();
let config = Config::parse_from([
"context-creator",
"--prompt",
" \t \n ",
src_dir.to_str().unwrap(),
]);
assert_eq!(config.get_prompt(), None); assert_eq!(config.paths, Some(vec![src_dir]));
assert!(config.validate().is_ok());
}
#[test]
fn test_very_long_prompt_with_paths() {
let temp_dir = TempDir::new().unwrap();
let src_dir = temp_dir.path().join("src");
let tests_dir = temp_dir.path().join("tests");
std::fs::create_dir(&src_dir).unwrap();
std::fs::create_dir(&tests_dir).unwrap();
let long_prompt = "a".repeat(10000);
let config = Config::parse_from([
"context-creator",
"--prompt",
&long_prompt,
src_dir.to_str().unwrap(),
tests_dir.to_str().unwrap(),
]);
assert_eq!(config.get_prompt(), Some(long_prompt));
assert_eq!(config.paths, Some(vec![src_dir, tests_dir]));
assert!(config.validate().is_ok());
}
#[test]
fn test_unicode_prompt_with_paths() {
let temp_dir = TempDir::new().unwrap();
let src_dir = temp_dir.path().join("src");
std::fs::create_dir(&src_dir).unwrap();
let unicode_prompt = "分析这个代码库 🚀 Analyze this codebase";
let config = Config::parse_from([
"context-creator",
"--prompt",
unicode_prompt,
src_dir.to_str().unwrap(),
]);
assert_eq!(config.get_prompt(), Some(unicode_prompt.to_string()));
assert_eq!(config.paths, Some(vec![src_dir]));
assert!(config.validate().is_ok());
}
#[test]
fn test_many_include_patterns_with_prompt() {
let config = Config::parse_from([
"context-creator",
"--prompt",
"Find all file types",
"--include",
"**/*.rs",
"--include",
"**/*.py",
"--include",
"**/*.js",
"--include",
"**/*.ts",
"--include",
"**/*.go",
"--include",
"**/*.java",
"--include",
"**/*.cpp",
"--include",
"**/*.c",
"--include",
"**/*.h",
"--include",
"**/*.hpp",
"--include",
"**/*.toml",
"--include",
"**/*.json",
"--include",
"**/*.yaml",
"--include",
"**/*.yml",
"--include",
"**/*.md",
]);
assert_eq!(config.get_prompt(), Some("Find all file types".to_string()));
assert_eq!(config.get_include_patterns().len(), 15);
assert!(config
.get_include_patterns()
.contains(&"**/*.rs".to_string()));
assert!(config
.get_include_patterns()
.contains(&"**/*.md".to_string()));
assert!(config.validate().is_ok());
}
#[test]
fn test_conflicting_include_ignore_patterns() {
let config = Config::parse_from([
"context-creator",
"--prompt",
"Test conflicting patterns",
"--include",
"src/**/*.rs",
"--ignore",
"src/**/*.rs", ]);
assert_eq!(
config.get_prompt(),
Some("Test conflicting patterns".to_string())
);
assert_eq!(config.get_include_patterns(), vec!["src/**/*.rs"]);
assert_eq!(config.get_ignore_patterns(), vec!["src/**/*.rs"]);
assert!(config.validate().is_ok());
}
#[test]
fn test_prompt_with_output_file_should_fail() {
let config = Config::parse_from([
"context-creator",
"--prompt",
"This should fail",
"--output-file",
"output.md",
"src/",
]);
assert_eq!(config.get_prompt(), Some("This should fail".to_string()));
assert_eq!(config.output_file, Some(PathBuf::from("output.md")));
assert!(config.validate().is_err());
}
#[test]
fn test_copy_with_output_file_should_fail() {
let config = Config::parse_from([
"context-creator",
"--copy",
"--output-file",
"output.md",
"src/",
]);
assert!(config.copy);
assert_eq!(config.output_file, Some(PathBuf::from("output.md")));
assert!(config.validate().is_err());
}
#[test]
fn test_no_input_source_should_fail() {
let config = Config::parse_from(["context-creator", "--max-tokens", "100000", "--verbose"]);
assert_eq!(config.get_prompt(), None);
assert_eq!(config.paths, None);
assert_eq!(config.include, None);
assert_eq!(config.remote, None);
assert!(!config.read_stdin);
assert!(config.validate().is_err());
}
#[test]
fn test_existing_usage_patterns_still_work() {
let config1 = Config::parse_from(["context-creator", "src/"]);
assert!(config1.validate().is_ok());
let config2 = Config::parse_from(["context-creator", "--prompt", "Analyze"]);
assert!(config2.validate().is_ok());
let config3 = Config::parse_from(["context-creator", "--include", "**/*.rs"]);
assert!(config3.validate().is_ok());
let config4 = Config::parse_from([
"context-creator",
"--remote",
"https://github.com/owner/repo",
]);
assert!(config4.validate().is_ok());
let config5 = Config::parse_from([
"context-creator",
"--prompt",
"Test",
"--include",
"**/*.rs",
]);
assert!(config5.validate().is_ok());
}
#[test]
fn test_prompt_with_existing_directories() {
let temp_dir = TempDir::new().unwrap();
let src_dir = temp_dir.path().join("src");
let tests_dir = temp_dir.path().join("tests");
std::fs::create_dir(&src_dir).unwrap();
std::fs::create_dir(&tests_dir).unwrap();
let config = Config::parse_from([
"context-creator",
"--prompt",
"Analyze real directories",
src_dir.to_str().unwrap(),
tests_dir.to_str().unwrap(),
]);
assert_eq!(
config.get_prompt(),
Some("Analyze real directories".to_string())
);
assert_eq!(config.paths, Some(vec![src_dir.clone(), tests_dir.clone()]));
assert!(config.validate().is_ok());
}
#[test]
fn test_stdin_with_nonexistent_directories_should_fail() {
let config = Config::parse_from([
"context-creator",
"--stdin",
"/nonexistent/directory",
"/another/nonexistent/directory",
]);
assert!(config.read_stdin);
assert_eq!(
config.paths,
Some(vec![
PathBuf::from("/nonexistent/directory"),
PathBuf::from("/another/nonexistent/directory")
])
);
assert!(config.validate().is_err());
}
#[test]
fn test_prompt_with_file_instead_of_directory_should_pass() {
let temp_dir = TempDir::new().unwrap();
let file_path = temp_dir.path().join("file.txt");
std::fs::write(&file_path, "test content").unwrap();
let config = Config::parse_from([
"context-creator",
"--prompt",
"This should pass",
file_path.to_str().unwrap(),
]);
assert_eq!(config.get_prompt(), Some("This should pass".to_string()));
assert_eq!(config.paths, Some(vec![file_path]));
assert!(config.validate().is_ok());
}
#[test]
fn test_maximum_command_line_length() {
let mut args = vec![
"context-creator",
"--prompt",
"Test maximum command line length",
"--max-tokens",
"1000000",
"--tool",
"gemini",
"--verbose",
"--progress",
"--enhanced-context",
"--trace-imports",
"--include-callers",
"--include-types",
"--semantic-depth",
"5",
];
for i in 0..100 {
args.push("--include");
args.push(Box::leak(format!("pattern{i}/**/*.rs").into_boxed_str()));
}
for i in 0..100 {
args.push("--ignore");
args.push(Box::leak(format!("ignore{i}/**").into_boxed_str()));
}
let config = Config::parse_from(args);
assert_eq!(
config.get_prompt(),
Some("Test maximum command line length".to_string())
);
assert_eq!(config.get_include_patterns().len(), 100);
assert_eq!(config.get_ignore_patterns().len(), 100);
assert!(config.validate().is_ok());
}
#[test]
fn test_zero_length_patterns() {
let config = Config::parse_from([
"context-creator",
"--prompt",
"Test empty patterns",
"--include",
"",
"--ignore",
"",
]);
assert_eq!(config.get_prompt(), Some("Test empty patterns".to_string()));
assert_eq!(config.get_include_patterns(), vec![""]);
assert_eq!(config.get_ignore_patterns(), vec![""]);
assert!(config.validate().is_ok());
}