Ctrl K
ComunidadGuías…

tests/toolkit/test_shell_rules.py

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
"""The pure shell-command classification: the metacharacter and expansion
checks, subcommand splitting, the long-leading-``sleep`` check and the
files a command shows."""

from __future__ import annotations

from typing import Any

import pytest

from noeta.toolkit.shell import (
    has_shell_meta,
    has_unquoted_expansion,
    long_leading_sleep,
    parse_argv,
    read_files,
    shell_reads,
    subcommands,
)


def test_meta_and_expansion_predicates() -> None:
    assert has_shell_meta("a; b") and has_shell_meta("a\nb") and not has_shell_meta("a b")
    assert has_unquoted_expansion("a*") and not has_unquoted_expansion("'a*'")
    assert has_unquoted_expansion("x=~/y") and not has_unquoted_expansion("HEAD~1")
    assert parse_argv("a 'b c'") == ["a", "b c"]
    assert parse_argv("a 'b") is None


# -- long leading sleep ------------------------------------------------------


@pytest.mark.parametrize(
    ("command", "described"),
    [
        ("sleep 110", "standalone sleep 110"),
        ("sleep 25", "standalone sleep 25"),
        ("sleep 30.5", "standalone sleep 30.5"),
        ("sleep 600 && tail -5 out.log", "sleep 600 followed by: tail -5 out.log"),
        ("  sleep 120; cat a | grep b", "sleep 120 followed by: cat a grep b"),
        ("sleep 24", None),
        ("sleep 5 && make", None),
        ("echo hi && sleep 300", None),
        ("sleep 1m", None),
        ("until test -f done; do sleep 2; done", None),
        ("echo 'sleep 300; x'", None),
    ],
)
def test_long_leading_sleep_matches_claude_code(command: str, described: Any) -> None:
    assert long_leading_sleep(command) == described


def test_subcommands_split_outside_quotes_only() -> None:
    assert subcommands("a && b || c; d | e & f") == ["a", "b", "c", "d", "e", "f"]
    assert subcommands("echo 'a; b' && echo \"c \\\" ; d\"") == [
        "echo 'a; b'",
        'echo "c \\" ; d"',
    ]
    assert subcommands("  ") == []


@pytest.mark.parametrize(("command", "files"), [
    ("cat src/a.py", [("src/a.py", False)]),
    ("cat -n a.py", [("a.py", False)]),
    ("nl a.py", [("a.py", False)]),
    ("bat -p a.py", [("a.py", False)]),
    ("cat a.py && echo --- && cat 'b c.py'; cat d.py",
     [("a.py", False), ("b c.py", False), ("d.py", False)]),
    ("head -n 20 a.py", [("a.py", False)]),
    ("head -5 a.py", [("a.py", False)]),
    ("tail --lines=3 a.py", [("a.py", False)]),
    ("sed -n '3,9p' a.py", [("a.py", False)]),
    ("sed -n 4p a.py", [("a.py", False)]),
    ("grep -n TODO a.py", [("a.py", True)]),
    ("rg -i -C 2 todo a.py", [("a.py", True)]),
    # Not counted: pipes and redirections, two files to one program,
    # another program in the chain, a sed that is not a quiet range print,
    # grep beside anything else, a glob, stdin.
    ("cat a.py | head", []),
    ("cat a.py > b.py", []),
    ("cat a.py b.py", []),
    ("cat a.py && ls", []),
    ("ls; cat a.py", []),
    ("sed -i '1p' a.py", []),
    ("sed '1,2p' a.py", []),
    ("sed -n 's/a/b/p' a.py", []),
    ("head -n 0 a.py", []),
    ("cat -A a.py", []),
    ("cat -", []),
    ("grep x a.py && cat b.py", []),
    ("grep x *.py", []),
    ("grep -r x src", []),
    ("echo hi", []),
    ("cat 'unbalanced", []),
])
def test_files_a_command_shows(command: str, files: list[tuple[str, bool]]) -> None:
    """Claude Code's count of the files a ``Bash`` call read (its
    read-file state takes them in, so a compaction restores them)."""
    assert read_files(command) == files


@pytest.mark.parametrize(("command", "shown"), [
    ("cat f", ("a\nb\nc\n", 0, 0)),
    ("head -n 2 f", ("a\nb", 1, 2)),
    ("head f", ("a\nb\nc\n", 1, 10)),
    ("tail -n 2 f", ("b\nc", 2, 2)),
    ("tail -9 f", ("a\nb\nc", 1, 3)),
    ("sed -n 2,3p f", ("b\nc", 2, 2)),
    ("sed -n 3,2p f", ("c", 3, 1)),
    ("sed -n 9p f", None),
])
def test_the_part_of_a_file_a_command_shows(command: str,
                                            shown: tuple[str, int, int] | None) -> None:
    """Claude Code's view of the file for its read-file entry: the lines
    shown, the first of them and how many were asked for."""
    [read] = shell_reads(command)
    assert read.view("a\nb\nc\n") == shown


def test_what_a_command_printed_holds_the_view() -> None:
    [cat] = shell_reads("cat f")
    assert cat.shown_in("x\r\na\r\nb\r\n", "\n\na\nb\n  ")
    assert not cat.shown_in("a\nc", "a\nb")
    [numbered] = shell_reads("cat -n f")
    assert numbered.numbered and numbered.shown_in("     1\ta\n     2\t\n     3\tb", "a\n\nb")
    assert numbered.file_text("     1\ta\n     2\tb") == "a\nb"
    assert not cat.numbered and cat.file_text("a\r\nb") == "a\nb"
    [grep] = shell_reads("grep -n x f")
    assert grep.grep and not grep.numbered