//! Integration tests for the `ctxpack` command. use assert_cmd::Command; use predicates::prelude::*; use std::path::PathBuf; fn cargo_command() -> Command { Command::cargo_bin("ctxpack").expect("binary") } fn fixture_path(relative: &str) -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("..") .join("..") .join("fixtures") .join("polyglot") .join("repo") .join(relative) } fn pwsh_command(script: impl AsRef) -> Command { let mut command = Command::new("pwsh"); command .arg("-NoProfile") .arg("-Command") .arg(script.as_ref()); command } #[test] fn emits_definition_blocks_for_line_hits() { let mut command = cargo_command(); command .arg(format!("{}:26", fixture_path("src/lib.rs").display())) .assert() .success() .stdout(predicate::str::contains("kind=definition")) .stdout(predicate::str::contains("call_helper")); } #[test] fn emits_json_blocks_from_definition_records() { let mut command = cargo_command(); command .arg("--json") .arg("{\"path\":\"demo.rs\",\"qualified_name\":\"helper\",\"start_line\":1,\"end_line\":2,\"text\":\"fn helper() {}\"}") .assert() .failure(); } #[test] fn supports_powershell_pipeline_json_input() { let binary = assert_cmd::cargo::cargo_bin("ctxpack"); let input = "[{\"path\":\"demo.rs\",\"qualified_name\":\"helper\",\"start_line\":1,\"end_line\":2,\"text\":\"fn helper() {}\"}]"; let script = format!( "'{}' | & '{}' --input-format auto --json | ConvertFrom-Json | Select-Object -First 1 -ExpandProperty header", input, binary.display() ); let mut command = pwsh_command(script); command .assert() .success() .stdout(predicate::str::contains("helper")); } #[test] fn rejects_scalar_json_from_stdin_auto_mode() { let mut command = cargo_command(); command .args(["--json", "--input-format", "auto"]) .write_stdin("42\n") .assert() .failure() .stderr(predicate::str::contains( "JSON input must be an object or array of objects", )); } #[test] fn rejects_non_array_wrapper_fields_from_jsonl_stdin() { let mut command = cargo_command(); command .args(["--json", "--input-format", "jsonl"]) .write_stdin("{\"hits\":{\"path\":\"demo.rs\",\"line\":7}}\n") .assert() .failure() .stderr(predicate::str::contains( "context JSON field 'hits' must be an array when present", )); } #[test] fn expands_jsonl_wrappers_and_applies_dedupe_limits() { let input = concat!( "{\"items\":[{\"path\":\"demo.rs\",\"name\":\"first\",\"text\":\"fn first() {}\"}]}\n", "{\"path\":\"demo.rs\",\"name\":\"first\",\"text\":\"fn first() {}\"}\n", "{\"path\":\"demo.rs\",\"name\":\"second\",\"text\":\"fn second() {}\"}\n" ); let mut command = cargo_command(); command .args([ "--json", "--input-format", "jsonl", "--dedupe", "--sort", "path", "--max-blocks", "1", ]) .write_stdin(input) .assert() .success() .stdout(predicate::str::contains("\"header\":\"first\"")) .stdout(predicate::str::contains("\"header\":\"second\"").not()); } #[test] fn help_includes_examples_and_pipeline_usage() { let mut command = cargo_command(); command .arg("--help") .assert() .success() .stdout(predicate::str::contains("--max-blocks")) .stdout(predicate::str::contains("ConvertFrom-Json")) .stdout(predicate::str::contains("ctxpack")); }