| 
1 |  | -"""Tests for the gitingest cli."""  | 
 | 1 | +"""Tests for the Gitingest CLI."""  | 
2 | 2 | 
 
  | 
3 | 3 | import os  | 
4 | 4 | from inspect import signature  | 
 | 5 | +from pathlib import Path  | 
 | 6 | +from typing import List  | 
5 | 7 | 
 
  | 
6 |  | -from click.testing import CliRunner  | 
 | 8 | +import pytest  | 
 | 9 | +from _pytest.monkeypatch import MonkeyPatch  | 
 | 10 | +from click.testing import CliRunner, Result  | 
7 | 11 | 
 
  | 
8 | 12 | from gitingest.cli import main  | 
9 | 13 | from gitingest.config import MAX_FILE_SIZE, OUTPUT_FILE_NAME  | 
10 | 14 | 
 
  | 
11 | 15 | 
 
  | 
12 |  | -def test_cli_with_default_options():  | 
13 |  | -    runner = CliRunner()  | 
14 |  | -    result = runner.invoke(main, ["./"])  | 
15 |  | -    output_lines = result.output.strip().split("\n")  | 
16 |  | -    assert f"Analysis complete! Output written to: {OUTPUT_FILE_NAME}" in output_lines  | 
17 |  | -    assert os.path.exists(OUTPUT_FILE_NAME), f"Output file was not created at {OUTPUT_FILE_NAME}"  | 
18 |  | - | 
19 |  | -    os.remove(OUTPUT_FILE_NAME)  | 
20 |  | - | 
21 |  | - | 
22 |  | -def test_cli_with_options():  | 
23 |  | -    runner = CliRunner()  | 
24 |  | -    result = runner.invoke(  | 
25 |  | -        main,  | 
26 |  | -        [  | 
27 |  | -            "./",  | 
28 |  | -            "--output",  | 
29 |  | -            str(OUTPUT_FILE_NAME),  | 
30 |  | -            "--max-size",  | 
31 |  | -            str(MAX_FILE_SIZE),  | 
32 |  | -            "--exclude-pattern",  | 
33 |  | -            "tests/",  | 
34 |  | -            "--include-pattern",  | 
35 |  | -            "src/",  | 
36 |  | -        ],  | 
37 |  | -    )  | 
38 |  | -    output_lines = result.output.strip().split("\n")  | 
39 |  | -    assert f"Analysis complete! Output written to: {OUTPUT_FILE_NAME}" in output_lines  | 
40 |  | -    assert os.path.exists(OUTPUT_FILE_NAME), f"Output file was not created at {OUTPUT_FILE_NAME}"  | 
41 |  | - | 
42 |  | -    os.remove(OUTPUT_FILE_NAME)  | 
43 |  | - | 
44 |  | - | 
45 |  | -def test_cli_with_stdout_output():  | 
46 |  | -    """Test CLI invocation with output directed to STDOUT."""  | 
 | 16 | +@pytest.mark.parametrize(  | 
 | 17 | +    "cli_args, expect_file",  | 
 | 18 | +    [  | 
 | 19 | +        pytest.param(["./"], True, id="default-options"),  | 
 | 20 | +        pytest.param(  | 
 | 21 | +            [  | 
 | 22 | +                "./",  | 
 | 23 | +                "--output",  | 
 | 24 | +                str(OUTPUT_FILE_NAME),  | 
 | 25 | +                "--max-size",  | 
 | 26 | +                str(MAX_FILE_SIZE),  | 
 | 27 | +                "--exclude-pattern",  | 
 | 28 | +                "tests/",  | 
 | 29 | +                "--include-pattern",  | 
 | 30 | +                "src/",  | 
 | 31 | +            ],  | 
 | 32 | +            True,  | 
 | 33 | +            id="custom-options",  | 
 | 34 | +        ),  | 
 | 35 | +    ],  | 
 | 36 | +)  | 
 | 37 | +def test_cli_writes_file(tmp_path: Path, monkeypatch: MonkeyPatch, cli_args: list[str], expect_file: bool) -> None:  | 
 | 38 | +    """Run the CLI and verify that the SARIF file is created (or not)."""  | 
 | 39 | +    # Work inside an isolated temp directory  | 
 | 40 | +    monkeypatch.chdir(tmp_path)  | 
47 | 41 | 
 
  | 
48 |  | -    kwargs = {}  | 
49 |  | -    if "mix_stderr" in signature(CliRunner.__init__).parameters:  | 
50 |  | -        kwargs["mix_stderr"] = (  | 
51 |  | -            False  # Click < 8.2 (https://click.palletsprojects.com/en/stable/changes/#version-8-2-0)  | 
52 |  | -        )  | 
 | 42 | +    result = _invoke_isolated_cli_runner(cli_args)  | 
53 | 43 | 
 
  | 
54 |  | -    runner = CliRunner(**kwargs)  | 
55 |  | -    result = runner.invoke(main, ["./", "--output", "-", "--exclude-pattern", "tests/"])  | 
 | 44 | +    assert result.exit_code == 0, result.stderr  | 
56 | 45 | 
 
  | 
 | 46 | +    # Summary line should be on STDOUT  | 
 | 47 | +    stdout_lines = result.stdout.splitlines()  | 
 | 48 | +    assert f"Analysis complete! Output written to: {OUTPUT_FILE_NAME}" in stdout_lines  | 
 | 49 | + | 
 | 50 | +    # File side-effect  | 
 | 51 | +    sarif_file = tmp_path / OUTPUT_FILE_NAME  | 
 | 52 | +    assert sarif_file.exists() is expect_file, f"{OUTPUT_FILE_NAME} existence did not match expectation"  | 
 | 53 | + | 
 | 54 | + | 
 | 55 | +def test_cli_with_stdout_output() -> None:  | 
 | 56 | +    """Test CLI invocation with output directed to STDOUT."""  | 
 | 57 | +    result = _invoke_isolated_cli_runner(["./", "--output", "-", "--exclude-pattern", "tests/"])  | 
 | 58 | + | 
 | 59 | +    # ─── core expectations (stdout) ────────────────────────────────────-  | 
57 | 60 |     assert result.exit_code == 0, f"CLI exited with code {result.exit_code}, stderr: {result.stderr}"  | 
58 |  | -    assert "---" in result.output, "Expected file separator '---' not found in STDOUT"  | 
59 |  | -    assert "src/gitingest/cli.py" in result.output, "Expected content (e.g., src/gitingest/cli.py) not found in STDOUT"  | 
 | 61 | +    assert "---" in result.stdout, "Expected file separator '---' not found in STDOUT"  | 
 | 62 | +    assert "src/gitingest/cli.py" in result.stdout, "Expected content (e.g., src/gitingest/cli.py) not found in STDOUT"  | 
60 | 63 |     assert not os.path.exists(OUTPUT_FILE_NAME), f"Output file {OUTPUT_FILE_NAME} was unexpectedly created."  | 
61 |  | -    assert "Analysis complete!  Output sent to stdout." not in result.output  | 
62 |  | -    assert "Analysis complete!" in result.stderr, "Expected summary message 'Analysis complete!' not found in STDERR"  | 
63 |  | -    assert f"Output written to: {OUTPUT_FILE_NAME}" not in result.stderr  | 
 | 64 | + | 
 | 65 | +    # ─── the summary must *not* pollute STDOUT, must appear on STDERR ───  | 
 | 66 | +    summary = "Analysis complete! Output sent to stdout."  | 
 | 67 | +    stdout_lines = result.stdout.splitlines()  | 
 | 68 | +    stderr_lines = result.stderr.splitlines()  | 
 | 69 | +    assert summary not in stdout_lines, "Unexpected summary message found in STDOUT"  | 
 | 70 | +    assert summary in stderr_lines, "Expected summary message not found in STDERR"  | 
 | 71 | +    assert f"Output written to: {OUTPUT_FILE_NAME}" not in stderr_lines  | 
 | 72 | + | 
 | 73 | + | 
 | 74 | +def _invoke_isolated_cli_runner(args: List[str]) -> Result:  | 
 | 75 | +    """Return a CliRunner that keeps stderr apart on Click 8.0-8.1."""  | 
 | 76 | +    kwargs = {}  | 
 | 77 | +    if "mix_stderr" in signature(CliRunner.__init__).parameters:  | 
 | 78 | +        kwargs["mix_stderr"] = False  # Click 8.0–8.1  | 
 | 79 | +    runner = CliRunner(**kwargs)  | 
 | 80 | +    return runner.invoke(main, args)  | 
0 commit comments