-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathtest_cli_edge_cases.py
More file actions
156 lines (121 loc) · 5.62 KB
/
Copy pathtest_cli_edge_cases.py
File metadata and controls
156 lines (121 loc) · 5.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
"""Tests for __main__.py entry point and CLI edge cases."""
from __future__ import annotations
import json
import pytest
from deadcode.cli import cli
class TestMainModule:
"""Tests for __main__.py entry point (0% coverage)."""
@pytest.fixture
def runner(self):
from click.testing import CliRunner
return CliRunner()
def test_main_module_runs_help(self, runner):
"""python -m deadcode --help works (covers __main__.py:2-5)."""
result = runner.invoke(cli, ["--help"])
assert result.exit_code == 0
assert "Usage" in result.output
class TestCliEdgeCases:
"""Edge cases for CLI uncovered paths."""
@pytest.fixture
def runner(self):
from click.testing import CliRunner
return CliRunner()
def test_non_existent_project_exits_1(self, runner):
"""Scan with non-existent project exits 1 (cli.py:88-90)."""
result = runner.invoke(cli, ["--project", "/nonexistent/path", "scan"])
assert result.exit_code == 1
def test_fail_threshold_exits_high(self, runner, tmp_path):
"""--fail=0 exits 1 when findings exist (covers fail threshold path)."""
(tmp_path / "src" / "unused.ts").parent.mkdir(parents=True, exist_ok=True)
(tmp_path / "src" / "unused.ts").write_text(
"export function unused() { return 1; }\n"
)
result = runner.invoke(cli, ["-p", str(tmp_path), "scan", "--fail", "0"])
assert result.exit_code == 1
assert "FAIL" in result.output
def test_ignore_flag_before_subcommand(self, runner, tmp_path):
"""--ignore group option rejects submodule patterns (covers _merge_config_ignore)."""
(tmp_path / "src" / "used.ts").parent.mkdir(parents=True, exist_ok=True)
(tmp_path / "src" / "used.ts").write_text(
"export function used() { return 1; }\n"
)
(tmp_path / "src" / "unused.ts").parent.mkdir(parents=True, exist_ok=True)
(tmp_path / "src" / "unused.ts").write_text(
"export function unused() { return 2; }\n"
)
result = runner.invoke(
cli, ["-p", str(tmp_path), "--ignore", "**/unused.ts", "scan"]
)
assert result.exit_code == 0
assert "unused" not in result.output
class TestCliFormatOutput:
"""Tests for scan --format output modes (added in PR #34)."""
@pytest.fixture
def runner(self):
from click.testing import CliRunner
return CliRunner()
@pytest.fixture
def sample(self, tmp_path):
"""A tiny TS project with at least one dead export."""
mod = tmp_path / "src" / "mod.ts"
mod.parent.mkdir(parents=True, exist_ok=True)
mod.write_text(
"export function usedHelper() { return 1; }\n"
"export function unusedHelper() { return 2; }\n"
)
return tmp_path
def test_format_compact_output(self, runner, sample):
"""--format=compact produces one-line-per-finding output."""
result = runner.invoke(cli, ["-p", str(sample), "scan", "--format", "compact"])
assert result.exit_code == 0
assert "0 findings" not in result.output
assert "unusedHelper" in result.output
assert "unused_export" in result.output
def test_format_github_annotations(self, runner, sample):
"""--format=github produces ::warning/::error annotations."""
result = runner.invoke(cli, ["-p", str(sample), "scan", "--format", "github"])
assert result.exit_code == 0
assert "::warning" in result.output or "::error" in result.output
assert "unusedHelper" in result.output
def test_format_pretty_default(self, runner, sample):
"""Default pretty format shows table output."""
result = runner.invoke(cli, ["-p", str(sample), "scan", "--format", "pretty"])
assert result.exit_code == 0
assert "DeadCode Scan" in result.output
def test_legacy_json_output_still_works(self, runner, sample):
"""Legacy --json-output flag maps to --format=json."""
result = runner.invoke(cli, ["-p", str(sample), "scan", "--json-output"])
assert result.exit_code == 0
# Scanner details may contain newlines; use strict=False
payload = json.loads(result.output, strict=False)
assert "findings" in payload
assert "files_scanned" in payload
assert len(payload["findings"]) >= 1
class TestRemoveCommand:
"""Tests for the `remove` CLI command."""
@pytest.fixture
def runner(self):
from click.testing import CliRunner
return CliRunner()
def test_remove_dry_run_nothing_removable(self, runner, tmp_path):
"""remove --dry-run on clean project prints nothing removable."""
(tmp_path / "src" / "clean.ts").parent.mkdir(parents=True, exist_ok=True)
(tmp_path / "src" / "clean.ts").write_text("const x = 1;\n")
result = runner.invoke(cli, ["-p", str(tmp_path), "remove", "--dry-run"])
assert result.exit_code == 0
assert "Nothing removable" in result.output
class TestStatsCommand:
"""Tests for the `stats` CLI command."""
@pytest.fixture
def runner(self):
from click.testing import CliRunner
return CliRunner()
def test_stats_basic(self, runner, tmp_path):
"""stats command shows scan summary."""
(tmp_path / "src" / "unused.ts").parent.mkdir(parents=True, exist_ok=True)
(tmp_path / "src" / "unused.ts").write_text(
"export function unusedHelper() { return 1; }\n"
)
result = runner.invoke(cli, ["-p", str(tmp_path), "stats"])
assert result.exit_code == 0
assert "Files scanned" in result.output