claude-dev-env 1.29.3 → 1.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agents/code-quality-agent.md +279 -24
- package/agents/groq-coder.md +111 -0
- package/commands/plan.md +4 -5
- package/hooks/blocking/code_rules_enforcer.py +775 -8
- package/hooks/blocking/destructive_command_blocker.py +149 -12
- package/hooks/blocking/test_code_rules_enforcer.py +751 -0
- package/hooks/blocking/test_code_rules_enforcer_constant_equality.py +130 -0
- package/hooks/blocking/test_code_rules_enforcer_existence_checks.py +134 -0
- package/hooks/blocking/test_code_rules_enforcer_skip_decorators.py +150 -0
- package/hooks/blocking/test_destructive_command_blocker.py +281 -4
- package/hooks/git-hooks/test_config.py +9 -3
- package/hooks/git-hooks/test_gate_utils.py +9 -3
- package/hooks/git-hooks/test_pre_commit.py +9 -3
- package/hooks/git-hooks/test_pre_push.py +9 -3
- package/hooks/validators/run_all_validators.py +76 -3
- package/hooks/validators/test_output_formatter.py +4 -16
- package/hooks/validators/test_run_all_validators.py +22 -0
- package/hooks/validators/test_run_all_validators_integration.py +2 -11
- package/package.json +1 -1
- package/scripts/config/groq_bugteam_config.py +104 -0
- package/scripts/config/test_groq_bugteam_config.py +11 -0
- package/scripts/config/test_spec_implementer_prompt.py +36 -0
- package/scripts/groq_bugteam.README.md +2 -0
- package/scripts/groq_bugteam.py +74 -15
- package/scripts/groq_bugteam_dotenv.py +40 -0
- package/scripts/groq_bugteam_spec.py +226 -0
- package/scripts/test_groq_bugteam.py +143 -5
- package/scripts/test_groq_bugteam_apply_fix_from_spec.py +426 -0
- package/scripts/test_groq_bugteam_dotenv.py +66 -0
- package/scripts/test_groq_bugteam_spec.py +346 -0
- package/skills/bugteam/SKILL.md +4 -0
- package/skills/bugteam/reference/README.md +16 -0
- package/skills/bugteam/test_skill_additions.py +30 -0
- package/skills/monitor-open-prs/SKILL.md +104 -0
- package/skills/monitor-open-prs/scripts/discover_open_prs.py +69 -0
- package/skills/monitor-open-prs/scripts/test_discover_open_prs.py +149 -0
- package/skills/monitor-open-prs/test_skill_contract.py +43 -0
- package/skills/pr-review-responder/SKILL.md +10 -8
- package/hooks/github-action/pre-push-review.yml +0 -27
- package/hooks/github-action/test_workflow.py +0 -33
- package/skills/pr-review-responder/update_skill.py +0 -297
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"""Tests for constant-value test detection.
|
|
2
|
+
|
|
3
|
+
Tests that assert a constant equals a literal cover nothing about
|
|
4
|
+
behavior and should be deleted.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import importlib.util
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from types import ModuleType
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _load_enforcer_module() -> ModuleType:
|
|
15
|
+
module_path = Path(__file__).parent / "code_rules_enforcer.py"
|
|
16
|
+
spec = importlib.util.spec_from_file_location("code_rules_enforcer", module_path)
|
|
17
|
+
assert spec is not None
|
|
18
|
+
assert spec.loader is not None
|
|
19
|
+
module = importlib.util.module_from_spec(spec)
|
|
20
|
+
spec.loader.exec_module(module)
|
|
21
|
+
return module
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
code_rules_enforcer = _load_enforcer_module()
|
|
25
|
+
|
|
26
|
+
TEST_FILE_PATH = "packages/app/tests/test_constants.py"
|
|
27
|
+
PRODUCTION_FILE_PATH = "packages/app/services/feature.py"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_should_flag_test_asserting_constant_equals_literal() -> None:
|
|
31
|
+
source = (
|
|
32
|
+
'CACHE_DIR = "cache"\n'
|
|
33
|
+
"\n"
|
|
34
|
+
"def test_cache_dir_value() -> None:\n"
|
|
35
|
+
' assert CACHE_DIR == "cache"\n'
|
|
36
|
+
)
|
|
37
|
+
issues = code_rules_enforcer.check_constant_equality_tests(source, TEST_FILE_PATH)
|
|
38
|
+
assert any("constant" in issue.lower() for issue in issues), (
|
|
39
|
+
f"Expected constant-value test issue, got: {issues}"
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_should_flag_test_with_single_upper_snake_equality() -> None:
|
|
44
|
+
source = (
|
|
45
|
+
"MAX_SIZE = 100\n"
|
|
46
|
+
"\n"
|
|
47
|
+
"def test_max_size_is_100() -> None:\n"
|
|
48
|
+
" assert MAX_SIZE == 100\n"
|
|
49
|
+
)
|
|
50
|
+
issues = code_rules_enforcer.check_constant_equality_tests(source, TEST_FILE_PATH)
|
|
51
|
+
assert any("constant" in issue.lower() for issue in issues), (
|
|
52
|
+
f"Expected constant-value test issue, got: {issues}"
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_should_not_flag_test_asserting_computed_result() -> None:
|
|
57
|
+
source = (
|
|
58
|
+
"def test_compute_returns_expected() -> None:\n"
|
|
59
|
+
" computed_value = compute()\n"
|
|
60
|
+
" assert computed_value == EXPECTED_OUTPUT\n"
|
|
61
|
+
)
|
|
62
|
+
issues = code_rules_enforcer.check_constant_equality_tests(source, TEST_FILE_PATH)
|
|
63
|
+
assert issues == [], f"Expected no issues for behavior test, got: {issues}"
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_should_not_flag_test_with_multiple_assertions() -> None:
|
|
67
|
+
source = (
|
|
68
|
+
"TIMEOUT = 30\n"
|
|
69
|
+
"\n"
|
|
70
|
+
"def test_timeout_used_in_connection() -> None:\n"
|
|
71
|
+
" assert TIMEOUT == 30\n"
|
|
72
|
+
" connection = connect(timeout=TIMEOUT)\n"
|
|
73
|
+
" assert connection.is_alive()\n"
|
|
74
|
+
)
|
|
75
|
+
issues = code_rules_enforcer.check_constant_equality_tests(source, TEST_FILE_PATH)
|
|
76
|
+
assert issues == [], f"Expected no issues for multi-assertion test, got: {issues}"
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def test_should_not_flag_in_production_files() -> None:
|
|
80
|
+
source = (
|
|
81
|
+
"CACHE_DIR = 'cache'\n"
|
|
82
|
+
"\n"
|
|
83
|
+
"def test_something() -> None:\n"
|
|
84
|
+
" assert CACHE_DIR == 'cache'\n"
|
|
85
|
+
)
|
|
86
|
+
issues = code_rules_enforcer.check_constant_equality_tests(
|
|
87
|
+
source, PRODUCTION_FILE_PATH
|
|
88
|
+
)
|
|
89
|
+
assert issues == [], f"Expected no issues in production file, got: {issues}"
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def test_should_include_line_number_in_issue() -> None:
|
|
93
|
+
source = (
|
|
94
|
+
'\n\ndef test_my_constant_value() -> None:\n assert MY_CONST == "value"\n'
|
|
95
|
+
)
|
|
96
|
+
issues = code_rules_enforcer.check_constant_equality_tests(source, TEST_FILE_PATH)
|
|
97
|
+
assert any("Line 3" in issue for issue in issues), (
|
|
98
|
+
f"Expected 'Line 3' in issue, got: {issues}"
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def test_should_not_flag_non_test_function() -> None:
|
|
103
|
+
source = (
|
|
104
|
+
"TIMEOUT = 30\n"
|
|
105
|
+
"\n"
|
|
106
|
+
"def check_constant_value() -> None:\n"
|
|
107
|
+
" assert TIMEOUT == 30\n"
|
|
108
|
+
)
|
|
109
|
+
issues = code_rules_enforcer.check_constant_equality_tests(source, TEST_FILE_PATH)
|
|
110
|
+
assert issues == [], f"Expected no issues for non-test function, got: {issues}"
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def test_stops_at_max_issues_per_check() -> None:
|
|
114
|
+
source = (
|
|
115
|
+
"ALPHA = 1\nBETA = 2\nGAMMA = 3\nDELTA = 4\nEPSILON = 5\n"
|
|
116
|
+
"\n"
|
|
117
|
+
"def test_alpha() -> None:\n assert ALPHA == 1\n"
|
|
118
|
+
"\n"
|
|
119
|
+
"def test_beta() -> None:\n assert BETA == 2\n"
|
|
120
|
+
"\n"
|
|
121
|
+
"def test_gamma() -> None:\n assert GAMMA == 3\n"
|
|
122
|
+
"\n"
|
|
123
|
+
"def test_delta() -> None:\n assert DELTA == 4\n"
|
|
124
|
+
"\n"
|
|
125
|
+
"def test_epsilon() -> None:\n assert EPSILON == 5\n"
|
|
126
|
+
)
|
|
127
|
+
issues = code_rules_enforcer.check_constant_equality_tests(source, TEST_FILE_PATH)
|
|
128
|
+
assert len(issues) == code_rules_enforcer.MAX_ISSUES_PER_CHECK, (
|
|
129
|
+
f"Expected exactly MAX_ISSUES_PER_CHECK issues, got {len(issues)}: {issues}"
|
|
130
|
+
)
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"""Tests for existence-check test detection.
|
|
2
|
+
|
|
3
|
+
Tests that only assert callable(x), hasattr(m, 'name'), or
|
|
4
|
+
x is not None without any behavior validation are useless and
|
|
5
|
+
should be deleted or replaced.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import importlib.util
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from types import ModuleType
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _load_enforcer_module() -> ModuleType:
|
|
16
|
+
module_path = Path(__file__).parent / "code_rules_enforcer.py"
|
|
17
|
+
spec = importlib.util.spec_from_file_location("code_rules_enforcer", module_path)
|
|
18
|
+
assert spec is not None
|
|
19
|
+
assert spec.loader is not None
|
|
20
|
+
module = importlib.util.module_from_spec(spec)
|
|
21
|
+
spec.loader.exec_module(module)
|
|
22
|
+
return module
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
code_rules_enforcer = _load_enforcer_module()
|
|
26
|
+
|
|
27
|
+
TEST_FILE_PATH = "packages/app/tests/test_feature.py"
|
|
28
|
+
PRODUCTION_FILE_PATH = "packages/app/services/feature.py"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_should_flag_test_with_only_callable_assertion() -> None:
|
|
32
|
+
source = "def test_function_exists() -> None:\n assert callable(my_function)\n"
|
|
33
|
+
issues = code_rules_enforcer.check_existence_check_tests(source, TEST_FILE_PATH)
|
|
34
|
+
assert any("existence" in issue.lower() for issue in issues), (
|
|
35
|
+
f"Expected existence-check issue, got: {issues}"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_should_flag_test_with_only_hasattr_assertion() -> None:
|
|
40
|
+
source = (
|
|
41
|
+
"def test_module_has_attribute() -> None:\n"
|
|
42
|
+
' assert hasattr(module, "my_attr")\n'
|
|
43
|
+
)
|
|
44
|
+
issues = code_rules_enforcer.check_existence_check_tests(source, TEST_FILE_PATH)
|
|
45
|
+
assert any("existence" in issue.lower() for issue in issues), (
|
|
46
|
+
f"Expected existence-check issue, got: {issues}"
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_should_flag_test_with_only_is_not_none_assertion() -> None:
|
|
51
|
+
source = (
|
|
52
|
+
"def test_instance_created() -> None:\n"
|
|
53
|
+
" instance = MyClass()\n"
|
|
54
|
+
" assert instance is not None\n"
|
|
55
|
+
)
|
|
56
|
+
issues = code_rules_enforcer.check_existence_check_tests(source, TEST_FILE_PATH)
|
|
57
|
+
assert any("existence" in issue.lower() for issue in issues), (
|
|
58
|
+
f"Expected existence-check issue for is-not-None test, got: {issues}"
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def test_should_not_flag_test_with_behavior_assertions() -> None:
|
|
63
|
+
source = (
|
|
64
|
+
"def test_function_returns_expected_value() -> None:\n"
|
|
65
|
+
" actual_result = compute(input_value)\n"
|
|
66
|
+
" assert actual_result == expected_value\n"
|
|
67
|
+
)
|
|
68
|
+
issues = code_rules_enforcer.check_existence_check_tests(source, TEST_FILE_PATH)
|
|
69
|
+
assert issues == [], f"Expected no issues for behavior test, got: {issues}"
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def test_should_not_flag_test_with_multiple_assertions() -> None:
|
|
73
|
+
source = (
|
|
74
|
+
"def test_function_callable_and_returns_int() -> None:\n"
|
|
75
|
+
" assert callable(my_function)\n"
|
|
76
|
+
" assert isinstance(my_function(), int)\n"
|
|
77
|
+
)
|
|
78
|
+
issues = code_rules_enforcer.check_existence_check_tests(source, TEST_FILE_PATH)
|
|
79
|
+
assert issues == [], f"Expected no issues for multi-assertion test, got: {issues}"
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def test_should_not_flag_existence_checks_in_production_files() -> None:
|
|
83
|
+
source = "def test_function_exists() -> None:\n assert callable(my_function)\n"
|
|
84
|
+
issues = code_rules_enforcer.check_existence_check_tests(
|
|
85
|
+
source, PRODUCTION_FILE_PATH
|
|
86
|
+
)
|
|
87
|
+
assert issues == [], f"Expected no issues in production file, got: {issues}"
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def test_should_include_line_number_in_issue() -> None:
|
|
91
|
+
source = "\n\ndef test_callable_check() -> None:\n assert callable(func)\n"
|
|
92
|
+
issues = code_rules_enforcer.check_existence_check_tests(source, TEST_FILE_PATH)
|
|
93
|
+
assert any("Line 3" in issue for issue in issues), (
|
|
94
|
+
f"Expected 'Line 3' in issue, got: {issues}"
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def test_should_not_flag_non_test_function_with_existence_check() -> None:
|
|
99
|
+
source = "def helper_with_callable_check() -> None:\n assert callable(func)\n"
|
|
100
|
+
issues = code_rules_enforcer.check_existence_check_tests(source, TEST_FILE_PATH)
|
|
101
|
+
assert issues == [], f"Expected no issues for non-test function, got: {issues}"
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def test_stops_at_max_issues_per_check() -> None:
|
|
105
|
+
source = (
|
|
106
|
+
"def test_one() -> None:\n assert callable(func_one)\n"
|
|
107
|
+
"\n"
|
|
108
|
+
"def test_two() -> None:\n assert callable(func_two)\n"
|
|
109
|
+
"\n"
|
|
110
|
+
"def test_three() -> None:\n assert callable(func_three)\n"
|
|
111
|
+
"\n"
|
|
112
|
+
"def test_four() -> None:\n assert callable(func_four)\n"
|
|
113
|
+
"\n"
|
|
114
|
+
"def test_five() -> None:\n assert callable(func_five)\n"
|
|
115
|
+
)
|
|
116
|
+
issues = code_rules_enforcer.check_existence_check_tests(source, TEST_FILE_PATH)
|
|
117
|
+
assert len(issues) == code_rules_enforcer.MAX_ISSUES_PER_CHECK, (
|
|
118
|
+
f"Expected exactly MAX_ISSUES_PER_CHECK issues, got {len(issues)}: {issues}"
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def test_should_not_flag_outer_test_when_nested_helper_contains_existence_check() -> None:
|
|
123
|
+
source = (
|
|
124
|
+
"def test_outer_behavior() -> None:\n"
|
|
125
|
+
" if True:\n"
|
|
126
|
+
" def nested_helper() -> None:\n"
|
|
127
|
+
" assert callable(some_function)\n"
|
|
128
|
+
" assert 1 + 1 == 2\n"
|
|
129
|
+
)
|
|
130
|
+
issues = code_rules_enforcer.check_existence_check_tests(source, TEST_FILE_PATH)
|
|
131
|
+
assert issues == [], (
|
|
132
|
+
f"Expected no issues: nested helper's callable assertion must not count"
|
|
133
|
+
f" against the outer test, got: {issues}"
|
|
134
|
+
)
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"""Tests for skip-decorator detection in test files.
|
|
2
|
+
|
|
3
|
+
Tests must fail on missing dependencies rather than silently skip.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import importlib.util
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from types import ModuleType
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _load_enforcer_module() -> ModuleType:
|
|
14
|
+
module_path = Path(__file__).parent / "code_rules_enforcer.py"
|
|
15
|
+
spec = importlib.util.spec_from_file_location("code_rules_enforcer", module_path)
|
|
16
|
+
assert spec is not None
|
|
17
|
+
assert spec.loader is not None
|
|
18
|
+
module = importlib.util.module_from_spec(spec)
|
|
19
|
+
spec.loader.exec_module(module)
|
|
20
|
+
return module
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
code_rules_enforcer = _load_enforcer_module()
|
|
24
|
+
|
|
25
|
+
TEST_FILE_PATH = "packages/app/tests/test_feature.py"
|
|
26
|
+
PRODUCTION_FILE_PATH = "packages/app/services/feature.py"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_should_flag_pytest_mark_skip_on_test_function() -> None:
|
|
30
|
+
source = (
|
|
31
|
+
"import pytest\n"
|
|
32
|
+
"\n"
|
|
33
|
+
"@pytest.mark.skip(reason='missing dep')\n"
|
|
34
|
+
"def test_something() -> None:\n"
|
|
35
|
+
" assert True\n"
|
|
36
|
+
)
|
|
37
|
+
issues = code_rules_enforcer.check_skip_decorators_in_tests(source, TEST_FILE_PATH)
|
|
38
|
+
assert any("skip" in issue.lower() for issue in issues), (
|
|
39
|
+
f"Expected skip decorator issue, got: {issues}"
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_should_flag_pytest_mark_skipif_on_test_function() -> None:
|
|
44
|
+
source = (
|
|
45
|
+
"import pytest\n"
|
|
46
|
+
"\n"
|
|
47
|
+
"@pytest.mark.skipif(condition, reason='missing')\n"
|
|
48
|
+
"def test_other() -> None:\n"
|
|
49
|
+
" pass\n"
|
|
50
|
+
)
|
|
51
|
+
issues = code_rules_enforcer.check_skip_decorators_in_tests(source, TEST_FILE_PATH)
|
|
52
|
+
assert any("skip" in issue.lower() for issue in issues), (
|
|
53
|
+
f"Expected skipif decorator issue, got: {issues}"
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_should_flag_unittest_skip_on_test_function() -> None:
|
|
58
|
+
source = (
|
|
59
|
+
"import unittest\n"
|
|
60
|
+
"\n"
|
|
61
|
+
"@unittest.skip('not ready')\n"
|
|
62
|
+
"def test_thing() -> None:\n"
|
|
63
|
+
" pass\n"
|
|
64
|
+
)
|
|
65
|
+
issues = code_rules_enforcer.check_skip_decorators_in_tests(source, TEST_FILE_PATH)
|
|
66
|
+
assert any("skip" in issue.lower() for issue in issues), (
|
|
67
|
+
f"Expected unittest.skip issue, got: {issues}"
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def test_should_flag_skip_if_missing_dependency_decorator() -> None:
|
|
72
|
+
source = (
|
|
73
|
+
"@skip_if_missing_dependency('requests')\n"
|
|
74
|
+
"def test_with_requests() -> None:\n"
|
|
75
|
+
" pass\n"
|
|
76
|
+
)
|
|
77
|
+
issues = code_rules_enforcer.check_skip_decorators_in_tests(source, TEST_FILE_PATH)
|
|
78
|
+
assert any("skip" in issue.lower() for issue in issues), (
|
|
79
|
+
f"Expected skip_if_missing_dependency issue, got: {issues}"
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def test_should_not_flag_skip_decorators_on_non_test_functions() -> None:
|
|
84
|
+
source = (
|
|
85
|
+
"@pytest.mark.skip(reason='broken')\ndef helper_function() -> None:\n pass\n"
|
|
86
|
+
)
|
|
87
|
+
issues = code_rules_enforcer.check_skip_decorators_in_tests(source, TEST_FILE_PATH)
|
|
88
|
+
assert issues == [], f"Expected no issues for non-test function, got: {issues}"
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def test_should_not_flag_skip_decorators_in_production_files() -> None:
|
|
92
|
+
source = (
|
|
93
|
+
"@pytest.mark.skip(reason='missing dep')\n"
|
|
94
|
+
"def test_something() -> None:\n"
|
|
95
|
+
" assert True\n"
|
|
96
|
+
)
|
|
97
|
+
issues = code_rules_enforcer.check_skip_decorators_in_tests(
|
|
98
|
+
source, PRODUCTION_FILE_PATH
|
|
99
|
+
)
|
|
100
|
+
assert issues == [], f"Expected no issues in production file, got: {issues}"
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def test_should_include_line_number_in_issue_message() -> None:
|
|
104
|
+
source = (
|
|
105
|
+
"import pytest\n"
|
|
106
|
+
"\n"
|
|
107
|
+
"@pytest.mark.skip(reason='missing dep')\n"
|
|
108
|
+
"def test_flagged() -> None:\n"
|
|
109
|
+
" pass\n"
|
|
110
|
+
)
|
|
111
|
+
issues = code_rules_enforcer.check_skip_decorators_in_tests(source, TEST_FILE_PATH)
|
|
112
|
+
assert any("Line 3" in issue for issue in issues), (
|
|
113
|
+
f"Expected 'Line 3' in issue, got: {issues}"
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def test_should_flag_decorator_with_skip_in_name() -> None:
|
|
118
|
+
source = "@skip_slow_tests\ndef test_slow() -> None:\n pass\n"
|
|
119
|
+
issues = code_rules_enforcer.check_skip_decorators_in_tests(source, TEST_FILE_PATH)
|
|
120
|
+
assert any("skip" in issue.lower() for issue in issues), (
|
|
121
|
+
f"Expected skip-named decorator issue, got: {issues}"
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def test_stops_at_max_issues_per_check() -> None:
|
|
126
|
+
source = (
|
|
127
|
+
"import pytest\n"
|
|
128
|
+
"\n"
|
|
129
|
+
"@pytest.mark.skip(reason='a')\n"
|
|
130
|
+
"def test_one() -> None:\n"
|
|
131
|
+
" pass\n"
|
|
132
|
+
"\n"
|
|
133
|
+
"@pytest.mark.skip(reason='b')\n"
|
|
134
|
+
"def test_two() -> None:\n"
|
|
135
|
+
" pass\n"
|
|
136
|
+
"\n"
|
|
137
|
+
"@pytest.mark.skip(reason='c')\n"
|
|
138
|
+
"def test_three() -> None:\n"
|
|
139
|
+
" pass\n"
|
|
140
|
+
"\n"
|
|
141
|
+
"@pytest.mark.skip(reason='d')\n"
|
|
142
|
+
"def test_four() -> None:\n"
|
|
143
|
+
" pass\n"
|
|
144
|
+
"\n"
|
|
145
|
+
"@pytest.mark.skip(reason='e')\n"
|
|
146
|
+
"def test_five() -> None:\n"
|
|
147
|
+
" pass\n"
|
|
148
|
+
)
|
|
149
|
+
issues = code_rules_enforcer.check_skip_decorators_in_tests(source, TEST_FILE_PATH)
|
|
150
|
+
assert len(issues) == code_rules_enforcer.MAX_ISSUES_PER_CHECK
|