use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
use tempfile::TempDir;
fn create_test_file(dir: &TempDir, name: &str, content: &str) -> PathBuf {
let path = dir.path().join(name);
let mut file = File::create(&path).unwrap();
writeln!(file, "{}", content).unwrap();
path
}
#[test]
fn test_context_flag_c_default() {
let dir = TempDir::new().unwrap();
let content = "line 1\nline 2\nline 3\nline 4\nline 5\nline 6\nline 7\n";
let file = create_test_file(&dir, "test.rs", content);
let output = std::process::Command::new(env!("CARGO_BIN_EXE_splice"))
.arg("query")
.arg("--db")
.arg(dir.path().join("db"))
.arg("--label")
.arg("rust")
.arg("--json")
.output()
.unwrap();
assert!(true);
}
#[test]
fn test_context_flag_a_only() {
let dir = TempDir::new().unwrap();
let content = "line 1\nline 2\nline 3\nline 4\nline 5\nline 6\nline 7\nline 8\n";
let file = create_test_file(&dir, "test.rs", content);
let output = std::process::Command::new(env!("CARGO_BIN_EXE_splice"))
.arg("query")
.arg("--db")
.arg(dir.path().join("db"))
.arg("--label")
.arg("rust")
.arg("-A")
.arg("5")
.arg("--json")
.output()
.unwrap();
assert!(true);
}
#[test]
fn test_context_flag_b_only() {
let dir = TempDir::new().unwrap();
let content = "line 1\nline 2\nline 3\nline 4\nline 5\n";
let file = create_test_file(&dir, "test.rs", content);
let output = std::process::Command::new(env!("CARGO_BIN_EXE_splice"))
.arg("query")
.arg("--db")
.arg(dir.path().join("db"))
.arg("--label")
.arg("rust")
.arg("-B")
.arg("2")
.arg("--json")
.output()
.unwrap();
assert!(true);
}
#[test]
fn test_context_flag_a_and_b_combination() {
let dir = TempDir::new().unwrap();
let content =
"line 1\nline 2\nline 3\nline 4\nline 5\nline 6\nline 7\nline 8\nline 9\nline 10\n";
let file = create_test_file(&dir, "test.rs", content);
let output = std::process::Command::new(env!("CARGO_BIN_EXE_splice"))
.arg("query")
.arg("--db")
.arg(dir.path().join("db"))
.arg("--label")
.arg("rust")
.arg("-A")
.arg("5")
.arg("-B")
.arg("2")
.arg("--json")
.output()
.unwrap();
assert!(true);
}
#[test]
fn test_context_flag_c_overrides_a_when_larger() {
let dir = TempDir::new().unwrap();
let content = (1..=20)
.map(|i| format!("line {}", i))
.collect::<Vec<_>>()
.join("\n");
let file = create_test_file(&dir, "test.rs", &content);
let output = std::process::Command::new(env!("CARGO_BIN_EXE_splice"))
.arg("query")
.arg("--db")
.arg(dir.path().join("db"))
.arg("--label")
.arg("rust")
.arg("-C")
.arg("10")
.arg("-A")
.arg("5")
.arg("--json")
.output()
.unwrap();
assert!(true);
}
#[test]
fn test_context_large_file_performance() {
use splice::context;
use std::time::Instant;
let dir = TempDir::new().unwrap();
let lines: Vec<String> = (1..=1000)
.map(|i| format!("line {}: some content here", i))
.collect();
let content = lines.join("\n");
let file = create_test_file(&dir, "large.rs", &content);
let start_offset = content.find("line 500").unwrap();
let end_offset = content.find("line 501").unwrap();
let started = Instant::now();
let ctx = context::extract_context_asymmetric(&file, start_offset, end_offset, 10, 10).unwrap();
let elapsed = started.elapsed();
assert!(!ctx.selected.is_empty());
println!("Context extraction on large file: {:?}", elapsed);
}
#[test]
fn test_json_context_before_after_arrays() {
use splice::context;
use std::io;
let dir = TempDir::new().unwrap();
let content = "line 1\nline 2\nline 3\nline 4\nline 5\nline 6\nline 7\nline 8";
let file = create_test_file(&dir, "test.rs", content);
let test_file = dir.path().join("test.rs");
let line3_start = content.find("line 3").unwrap();
let line4_end = content.find("line 5").unwrap();
let ctx =
context::extract_context_asymmetric(&test_file, line3_start, line4_end, 2, 3).unwrap();
assert_eq!(ctx.before.len(), 2, "Should have 2 lines before");
assert_eq!(ctx.after.len(), 3, "Should have 3 lines after");
assert!(ctx.before[0].contains("line 1") || ctx.before[0].contains("line 2"));
}
#[test]
fn test_json_context_with_b_flag() {
use splice::context;
let dir = TempDir::new().unwrap();
let content = "line 1\nline 2\nline 3\nline 4\nline 5\nline 6\nline 7";
let file = create_test_file(&dir, "test.rs", content);
let test_file = dir.path().join("test.rs");
let line4_start = content.find("line 4").unwrap();
let line4_end = content.find("line 5").unwrap();
let ctx =
context::extract_context_asymmetric(&test_file, line4_start, line4_end, 2, 0).unwrap();
let before: Vec<_> = ctx.before.iter().collect();
let after: Vec<_> = ctx.after.iter().collect();
assert_eq!(
before.len(),
2,
"With context_before=2, should have 2 lines before"
);
assert_eq!(
after.len(),
0,
"With context_after=0, after should be empty"
);
}
#[test]
fn test_json_context_with_c_flag() {
use splice::context;
let dir = TempDir::new().unwrap();
let content = "line 1\nline 2\nline 3\nline 4\nline 5\nline 6\nline 7\nline 8\nline 9\nline 10";
let file = create_test_file(&dir, "test.rs", content);
let test_file = dir.path().join("test.rs");
let line5_start = content.find("line 5").unwrap();
let line6_end = content.find("line 7").unwrap();
let ctx =
context::extract_context_asymmetric(&test_file, line5_start, line6_end, 3, 3).unwrap();
assert_eq!(
ctx.before.len(),
3,
"With context_before=3, should have 3 lines before"
);
assert_eq!(
ctx.after.len(),
3,
"With context_after=3, should have 3 lines after"
);
}
#[test]
fn test_context_at_file_start() {
use splice::context;
let dir = TempDir::new().unwrap();
let content = "line 1\nline 2\nline 3\nline 4\nline 5";
let file = create_test_file(&dir, "test.rs", content);
let test_file = dir.path().join("test.rs");
let line1_end = content.find("line 2").unwrap();
let ctx = context::extract_context_asymmetric(&test_file, 0, line1_end, 3, 3).unwrap();
assert_eq!(
ctx.before.len(),
0,
"Should have 0 lines before at file start"
);
assert!(ctx.after.len() <= 3, "Should have up to 3 lines after");
}
#[test]
fn test_context_at_file_end() {
use splice::context;
let dir = TempDir::new().unwrap();
let content = "line 1\nline 2\nline 3\nline 4\nline 5";
let file = create_test_file(&dir, "test.rs", content);
let test_file = dir.path().join("test.rs");
let line5_start = content.find("line 5").unwrap();
let ctx =
context::extract_context_asymmetric(&test_file, line5_start, content.len(), 3, 3).unwrap();
assert!(ctx.before.len() <= 3, "Should have up to 3 lines before");
assert_eq!(ctx.after.len(), 0, "Should have 0 lines after at file end");
}
#[test]
fn test_context_span_context_serialization() {
use splice::output::SpanContext;
let ctx = SpanContext {
before: vec!["line 1".to_string(), "line 2".to_string()],
selected: vec!["line 3".to_string()],
after: vec!["line 4".to_string(), "line 5".to_string()],
};
let json = serde_json::to_string(&ctx).unwrap();
let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
assert!(
parsed.get("before").is_some(),
"SpanContext should have 'before' key"
);
assert!(
parsed.get("after").is_some(),
"SpanContext should have 'after' key"
);
assert!(parsed["before"].is_array(), "'before' should be an array");
assert!(parsed["after"].is_array(), "'after' should be an array");
let before: Vec<&str> = parsed["before"]
.as_array()
.unwrap()
.iter()
.filter_map(|v| v.as_str())
.collect();
let after: Vec<&str> = parsed["after"]
.as_array()
.unwrap()
.iter()
.filter_map(|v| v.as_str())
.collect();
assert_eq!(before.len(), 2);
assert_eq!(after.len(), 2);
}
#[test]
fn test_resolve_context_counts() {
use splice::resolve_context_counts;
let (before, after) = resolve_context_counts(0, 0, 3);
assert_eq!(before, 3, "With only context_both=3, both should be 3");
assert_eq!(after, 3, "With only context_both=3, both should be 3");
let (before, after) = resolve_context_counts(2, 0, 0);
assert_eq!(before, 2, "With only context_before=2, before should be 2");
assert_eq!(after, 0, "With only context_before=2, after should be 0");
let (before, after) = resolve_context_counts(0, 5, 0);
assert_eq!(before, 0, "With only context_after=5, before should be 0");
assert_eq!(after, 5, "With only context_after=5, after should be 5");
let (before, after) = resolve_context_counts(2, 5, 0);
assert_eq!(before, 2, "With -B 2 -A 5, before should be 2");
assert_eq!(after, 5, "With -B 2 -A 5, after should be 5");
let (before, after) = resolve_context_counts(3, 5, 10);
assert_eq!(
before, 10,
"With -C 10 -B 3, before should be max(10, 3) = 10"
);
assert_eq!(
after, 10,
"With -C 10 -A 5, after should be max(10, 5) = 10"
);
let (before, after) = resolve_context_counts(0, 0, 0);
assert_eq!(before, 0, "With all zeros, before should be 0");
assert_eq!(after, 0, "With all zeros, after should be 0");
}
#[test]
fn test_context_with_multiline_content() {
use splice::context;
let dir = TempDir::new().unwrap();
let content = "header\n\
line 1\n\
line 2\n\
line 3\n\
line 4\n\
line 5\n\
footer\n";
let file = create_test_file(&dir, "test.rs", content);
let test_file = dir.path().join("test.rs");
let line2_start = content.find("line 2").unwrap();
let line4_end = content.find("footer").unwrap();
let ctx =
context::extract_context_asymmetric(&test_file, line2_start, line4_end, 1, 1).unwrap();
assert_eq!(ctx.before.len(), 1, "Should have 1 line before");
assert_eq!(ctx.after.len(), 1, "Should have 1 line after");
}
#[test]
fn test_context_empty_before() {
use splice::context;
let dir = TempDir::new().unwrap();
let content = "line 1\nline 2\nline 3\n";
let file = create_test_file(&dir, "test.rs", content);
let test_file = dir.path().join("test.rs");
let ctx = context::extract_context_asymmetric(&test_file, 0, 10, 5, 5).unwrap();
assert_eq!(ctx.before.len(), 0, "At file start, before should be empty");
assert!(ctx.after.len() <= 5, "After should be at most 5 lines");
}
#[test]
fn test_context_empty_after() {
use splice::context;
let dir = TempDir::new().unwrap();
let content = "line 1\nline 2\nline 3";
let file = create_test_file(&dir, "test.rs", content);
let test_file = dir.path().join("test.rs");
let ctx =
context::extract_context_asymmetric(&test_file, content.len() - 5, content.len(), 5, 5)
.unwrap();
assert!(ctx.before.len() <= 5, "Before should be at most 5 lines");
assert_eq!(ctx.after.len(), 0, "At file end, after should be empty");
}