26382a7ac6
CI / Clippy (push) Failing after 15m13s
CI / Test (ubuntu-latest) (push) Failing after 16m1s
CI / Test (macos-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Build (no embeddings / no ORT) (push) Has been cancelled
CI / Format (push) Has been cancelled
CI / Cookbook (Node) (push) Has been cancelled
CI / Pi Extension (Node) (push) Has been cancelled
CI / Rust SDK (lean-ctx-client) (push) Has been cancelled
CI / Embed SDK (lean-ctx-sdk) (push) Has been cancelled
CI / Python SDK (leanctx) (push) Has been cancelled
CI / Hermes Plugin (Python) (push) Has been cancelled
CI / SDK Conformance Matrix (push) Has been cancelled
CI / Coverage (push) Has been cancelled
CI / cargo-deny (push) Has been cancelled
CI / Adversarial Safety (push) Has been cancelled
CI / Benchmarks (push) Has been cancelled
CI / Output-Quality Gate (eval A/B) (push) Has been cancelled
CI / Documentation (push) Has been cancelled
CI / CI Green (push) Has been cancelled
JetBrains Plugin / Actionlint (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (rust) (push) Has been cancelled
JetBrains Plugin / Validation (push) Has been cancelled
JetBrains Plugin / Build (push) Has been cancelled
JetBrains Plugin / Test (push) Has been cancelled
Security Check / Security Scan (push) Has been cancelled
297 lines
9.2 KiB
Rust
297 lines
9.2 KiB
Rust
//! Scenario tests for the bug reported by daviddatu_:
|
|
//!
|
|
//! lean-ctx was rewriting `git commit` to `git cmt` via the terse abbreviation
|
|
//! dictionary, and PowerShell quoting was wrapping full command strings in
|
|
//! single quotes when passed as a single argument.
|
|
//!
|
|
//! These tests verify:
|
|
//! 1. Git subcommand words are NEVER abbreviated in compression output
|
|
//! 2. Git write-commands (commit/push/pull/merge/rebase) are verbatim (no compression)
|
|
//! 3. PowerShell `join_command` does not wrap full command strings with & '...'
|
|
|
|
use lean_ctx::core::terse::dictionaries::{DictLevel, GIT, apply_dictionaries};
|
|
use lean_ctx::shell::compress::{has_structural_output, is_verbatim_output};
|
|
use lean_ctx::shell::join_command_for;
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Scenario 1: Terse dictionary must never abbreviate git subcommands
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#[test]
|
|
fn scenario_commit_never_abbreviated_in_git_output() {
|
|
let typical_output = "[main abc1234] feat(result-sheets): add sheet\n 2 files changed, 15 insertions(+), 3 deletions(-)";
|
|
let result = apply_dictionaries(typical_output, DictLevel::Full);
|
|
assert!(
|
|
!result.contains("cmt"),
|
|
"git commit output must not contain 'cmt' abbreviation: {result}"
|
|
);
|
|
assert!(
|
|
result.contains("abc1234"),
|
|
"commit hash must be preserved: {result}"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn scenario_branch_never_abbreviated_in_status_output() {
|
|
let status_output = "On branch feature/result-sheets\nYour branch is up to date with 'origin/feature/result-sheets'.\n\nnothing to commit, working tree clean";
|
|
let result = apply_dictionaries(status_output, DictLevel::Full);
|
|
assert!(
|
|
result.contains("branch"),
|
|
"word 'branch' must survive in output: {result}"
|
|
);
|
|
assert!(
|
|
!result.contains(" br "),
|
|
"must not abbreviate 'branch' to 'br': {result}"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn scenario_merge_checkout_rebase_stash_never_abbreviated() {
|
|
let git_words = ["commit", "branch", "checkout", "merge", "stash", "rebase"];
|
|
for word in &git_words {
|
|
let text = format!("the {word} operation completed successfully");
|
|
let result = apply_dictionaries(&text, DictLevel::Full);
|
|
assert!(
|
|
result.contains(word),
|
|
"git subcommand '{word}' must NOT be abbreviated in output. Got: {result}"
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn scenario_git_dictionary_contains_no_subcommands() {
|
|
let git_subcommands = [
|
|
"commit",
|
|
"branch",
|
|
"checkout",
|
|
"merge",
|
|
"stash",
|
|
"rebase",
|
|
"push",
|
|
"pull",
|
|
"fetch",
|
|
"clone",
|
|
"tag",
|
|
"reset",
|
|
"bisect",
|
|
"log",
|
|
"diff",
|
|
"show",
|
|
"status",
|
|
"add",
|
|
"switch",
|
|
"cherry-pick",
|
|
"blame",
|
|
"remote",
|
|
];
|
|
for abbr in GIT {
|
|
assert!(
|
|
!git_subcommands.contains(&abbr.long),
|
|
"CRITICAL: GIT dictionary abbreviates subcommand '{}' → '{}'. \
|
|
Agents will misinterpret abbreviated output as valid commands!",
|
|
abbr.long,
|
|
abbr.short
|
|
);
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Scenario 2: Git write-commands must be verbatim (never compressed)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#[test]
|
|
fn scenario_git_commit_is_verbatim() {
|
|
assert!(
|
|
is_verbatim_output("git commit -m \"feat(result-sheets): add new sheet\""),
|
|
"git commit must be classified as verbatim"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn scenario_git_push_is_verbatim() {
|
|
assert!(
|
|
is_verbatim_output("git push origin feature/result-sheets"),
|
|
"git push must be classified as verbatim"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn scenario_git_pull_is_verbatim() {
|
|
assert!(
|
|
is_verbatim_output("git pull --rebase origin main"),
|
|
"git pull must be classified as verbatim"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn scenario_git_merge_is_verbatim() {
|
|
assert!(
|
|
is_verbatim_output("git merge --no-ff feature/result-sheets"),
|
|
"git merge must be classified as verbatim"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn scenario_git_rebase_is_verbatim() {
|
|
assert!(
|
|
is_verbatim_output("git rebase -i HEAD~3"),
|
|
"git rebase must be classified as verbatim"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn scenario_git_cherry_pick_is_verbatim() {
|
|
assert!(
|
|
is_verbatim_output("git cherry-pick abc1234"),
|
|
"git cherry-pick must be classified as verbatim"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn scenario_git_tag_is_verbatim() {
|
|
assert!(
|
|
is_verbatim_output("git tag -a v3.6.10 -m \"release\""),
|
|
"git tag must be classified as verbatim"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn scenario_git_reset_is_verbatim() {
|
|
assert!(
|
|
is_verbatim_output("git reset --hard HEAD~1"),
|
|
"git reset must be classified as verbatim"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn scenario_git_status_still_compressible() {
|
|
assert!(
|
|
!is_verbatim_output("git status"),
|
|
"git status should still be compressible (high-value compression)"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn scenario_git_log_still_compressible() {
|
|
assert!(
|
|
!is_verbatim_output("git log --oneline -20"),
|
|
"git log should still be compressible (high-value compression)"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn scenario_git_diff_is_structural_not_verbatim_directly() {
|
|
assert!(
|
|
has_structural_output("git diff --cached"),
|
|
"git diff should be structural"
|
|
);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Scenario 3: PowerShell quoting edge cases
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#[test]
|
|
fn scenario_powershell_single_full_command_passthrough() {
|
|
let args: Vec<String> = vec!["git commit -m \"feat(result-sheets): add sheet\"".into()];
|
|
let result = join_command_for(&args, "-Command");
|
|
assert!(
|
|
!result.starts_with("& '"),
|
|
"single full-command string must NOT be wrapped in & '...': {result}"
|
|
);
|
|
assert_eq!(
|
|
result, "git commit -m \"feat(result-sheets): add sheet\"",
|
|
"should pass through the command unchanged"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn scenario_powershell_split_args_still_quoted() {
|
|
let args: Vec<String> = vec![
|
|
"git".into(),
|
|
"commit".into(),
|
|
"-m".into(),
|
|
"feat(result-sheets): add sheet".into(),
|
|
];
|
|
let result = join_command_for(&args, "-Command");
|
|
assert!(
|
|
result.starts_with("& "),
|
|
"split args should use call operator: {result}"
|
|
);
|
|
assert!(result.contains("git"), "should contain git: {result}");
|
|
assert!(
|
|
result.contains("commit"),
|
|
"should contain commit (not abbreviated): {result}"
|
|
);
|
|
assert!(
|
|
result.contains("'feat(result-sheets): add sheet'"),
|
|
"special chars in commit message should be quoted: {result}"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn scenario_powershell_single_simple_command_uses_call_operator() {
|
|
let args: Vec<String> = vec!["git".into()];
|
|
let result = join_command_for(&args, "-Command");
|
|
assert_eq!(
|
|
result, "& git",
|
|
"single simple command should use call operator"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn scenario_powershell_parentheses_in_message_quoted() {
|
|
let args: Vec<String> = vec![
|
|
"git".into(),
|
|
"commit".into(),
|
|
"-m".into(),
|
|
"fix(auth): resolve login issue".into(),
|
|
];
|
|
let result = join_command_for(&args, "-Command");
|
|
assert!(
|
|
result.contains("'fix(auth): resolve login issue'"),
|
|
"parentheses must be quoted in PowerShell: {result}"
|
|
);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Scenario 4: End-to-end compression verification
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#[test]
|
|
fn scenario_compress_if_beneficial_skips_git_commit_output() {
|
|
let command = "git commit -m \"feat: add feature\"";
|
|
let output = "[main abc1234] feat: add feature\n 1 file changed, 5 insertions(+)\n";
|
|
let result = lean_ctx::shell::compress::compress_if_beneficial_pub(command, output);
|
|
assert_eq!(
|
|
result.trim(),
|
|
output.trim(),
|
|
"git commit output must NOT be compressed"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn scenario_compress_if_beneficial_skips_git_push_output() {
|
|
let command = "git push origin main";
|
|
let output = "Everything up-to-date\n";
|
|
let result = lean_ctx::shell::compress::compress_if_beneficial_pub(command, output);
|
|
assert_eq!(
|
|
result.trim(),
|
|
output.trim(),
|
|
"git push output must NOT be compressed"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn scenario_compress_if_beneficial_still_compresses_git_status() {
|
|
let command = "git status";
|
|
let output = "On branch main\nYour branch is up to date with 'origin/main'.\n\nChanges not staged for commit:\n (use \"git add <file>...\" to update what will be committed)\n\n\tmodified: src/main.rs\n\tmodified: src/lib.rs\n\tmodified: src/utils.rs\n\tmodified: src/config.rs\n\tmodified: src/server.rs\n\nno changes added to commit (use \"git add\" and/or \"git commit -a\")\n";
|
|
let result = lean_ctx::shell::compress::compress_if_beneficial_pub(command, output);
|
|
assert!(
|
|
result.len() < output.len(),
|
|
"git status should still be compressed (was {} → {} bytes)",
|
|
output.len(),
|
|
result.len()
|
|
);
|
|
}
|