myverdict 0.1.0__tar.gz
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.
- myverdict-0.1.0/PKG-INFO +72 -0
- myverdict-0.1.0/README.md +40 -0
- myverdict-0.1.0/myverdict.egg-info/PKG-INFO +72 -0
- myverdict-0.1.0/myverdict.egg-info/SOURCES.txt +40 -0
- myverdict-0.1.0/myverdict.egg-info/dependency_links.txt +1 -0
- myverdict-0.1.0/myverdict.egg-info/entry_points.txt +3 -0
- myverdict-0.1.0/myverdict.egg-info/requires.txt +9 -0
- myverdict-0.1.0/myverdict.egg-info/top_level.txt +2 -0
- myverdict-0.1.0/pyproject.toml +49 -0
- myverdict-0.1.0/setup.cfg +4 -0
- myverdict-0.1.0/tests/test_ast_utils.py +260 -0
- myverdict-0.1.0/tests/test_bob_integration.py +264 -0
- myverdict-0.1.0/tests/test_bob_mode_install.py +106 -0
- myverdict-0.1.0/tests/test_cli.py +309 -0
- myverdict-0.1.0/tests/test_dead_functions.py +548 -0
- myverdict-0.1.0/tests/test_diff.py +112 -0
- myverdict-0.1.0/tests/test_hallucinated_api.py +390 -0
- myverdict-0.1.0/tests/test_mcp_server.py +325 -0
- myverdict-0.1.0/tests/test_report.py +491 -0
- myverdict-0.1.0/tests/test_suppressed_exc.py +325 -0
- myverdict-0.1.0/tests/test_trace.py +402 -0
- myverdict-0.1.0/tests/test_vacuous_tests.py +206 -0
- myverdict-0.1.0/verdict/__init__.py +0 -0
- myverdict-0.1.0/verdict/_coverage_plugin.py +137 -0
- myverdict-0.1.0/verdict/_tracer_plugin.py +138 -0
- myverdict-0.1.0/verdict/ast_utils.py +117 -0
- myverdict-0.1.0/verdict/bob_integration/__init__.py +7 -0
- myverdict-0.1.0/verdict/bob_integration/slash_commands/__init__.py +3 -0
- myverdict-0.1.0/verdict/checks/__init__.py +3 -0
- myverdict-0.1.0/verdict/checks/coverage_delta.py +224 -0
- myverdict-0.1.0/verdict/checks/dead_functions.py +198 -0
- myverdict-0.1.0/verdict/checks/hallucinated_api.py +507 -0
- myverdict-0.1.0/verdict/checks/phantom_files.py +137 -0
- myverdict-0.1.0/verdict/checks/suppressed_exc.py +244 -0
- myverdict-0.1.0/verdict/checks/trace.py +217 -0
- myverdict-0.1.0/verdict/checks/vacuous_tests.py +310 -0
- myverdict-0.1.0/verdict/cli.py +574 -0
- myverdict-0.1.0/verdict/dashboard_cmd.py +330 -0
- myverdict-0.1.0/verdict/diff.py +75 -0
- myverdict-0.1.0/verdict/mcp_server.py +350 -0
- myverdict-0.1.0/verdict/report.py +197 -0
- myverdict-0.1.0/verdict/types.py +49 -0
myverdict-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: myverdict
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A lie detector for AI coding agents: audits diffs and traces what actually runs.
|
|
5
|
+
Author: verdict contributors
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/nshah271/verdict
|
|
8
|
+
Project-URL: Repository, https://github.com/nshah271/verdict
|
|
9
|
+
Project-URL: Issues, https://github.com/nshah271/verdict/issues
|
|
10
|
+
Keywords: ai,code-review,static-analysis,lie-detector,agents,claude,bob,mcp
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
14
|
+
Classifier: Topic :: Software Development :: Testing
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
21
|
+
Classifier: Operating System :: OS Independent
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
Requires-Dist: mcp>=1.0.0
|
|
25
|
+
Requires-Dist: click>=8.1
|
|
26
|
+
Requires-Dist: jedi>=0.19
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: ruff; extra == "dev"
|
|
29
|
+
Requires-Dist: pytest; extra == "dev"
|
|
30
|
+
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
31
|
+
Requires-Dist: pre-commit; extra == "dev"
|
|
32
|
+
|
|
33
|
+
# verdict
|
|
34
|
+
|
|
35
|
+
A lie detector for AI coding agents. Audits an AI-generated diff statically and traces what actually runs when the tests execute, then returns a single scorecard: `PASS`, `SUSPICIOUS`, or `LIED`.
|
|
36
|
+
|
|
37
|
+
Built for the IBM Bob hackathon (May 2026). Repo bootstrap in progress; the real README lands as part of P3.5.
|
|
38
|
+
|
|
39
|
+
## Install
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pip install myverdict
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
The PyPI package is named `myverdict` (the shorter `verdict-ai` slot was taken by an unrelated project before we shipped). The Python import is still `import verdict` and the CLI is still `verdict`.
|
|
46
|
+
|
|
47
|
+
## Quick start with IBM Bob
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# Install verdict's MCP server and Custom Mode globally so Bob sees them
|
|
51
|
+
# in every project on this machine:
|
|
52
|
+
verdict mcp-install --global
|
|
53
|
+
verdict bob-mode-install --global
|
|
54
|
+
|
|
55
|
+
# Restart Bob, then in any project switch to "Verifier" mode after a
|
|
56
|
+
# coding session or type /verify for a one-shot audit.
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Drop `--global` to install per-project under `.bob/` instead.
|
|
60
|
+
|
|
61
|
+
## Status
|
|
62
|
+
|
|
63
|
+
| Priority | Owner | Status |
|
|
64
|
+
|----------|-------|--------|
|
|
65
|
+
| P0.1 foundation (diff + AST) | Neel | not started |
|
|
66
|
+
| P0.2 dead function detection | Jacob | not started |
|
|
67
|
+
| P0.3 vacuous test detection | Alexie | not started |
|
|
68
|
+
| P0.4 CLI + scorecard | Neel | not started |
|
|
69
|
+
| P1.1 execution tracer | Neel | not started |
|
|
70
|
+
| P1.2 MCP server | Ben | not started |
|
|
71
|
+
|
|
72
|
+
See `verdict-spec.md` and `TEAM.md` (local to the team, not in this repo) for the full plan.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# verdict
|
|
2
|
+
|
|
3
|
+
A lie detector for AI coding agents. Audits an AI-generated diff statically and traces what actually runs when the tests execute, then returns a single scorecard: `PASS`, `SUSPICIOUS`, or `LIED`.
|
|
4
|
+
|
|
5
|
+
Built for the IBM Bob hackathon (May 2026). Repo bootstrap in progress; the real README lands as part of P3.5.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install myverdict
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The PyPI package is named `myverdict` (the shorter `verdict-ai` slot was taken by an unrelated project before we shipped). The Python import is still `import verdict` and the CLI is still `verdict`.
|
|
14
|
+
|
|
15
|
+
## Quick start with IBM Bob
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# Install verdict's MCP server and Custom Mode globally so Bob sees them
|
|
19
|
+
# in every project on this machine:
|
|
20
|
+
verdict mcp-install --global
|
|
21
|
+
verdict bob-mode-install --global
|
|
22
|
+
|
|
23
|
+
# Restart Bob, then in any project switch to "Verifier" mode after a
|
|
24
|
+
# coding session or type /verify for a one-shot audit.
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Drop `--global` to install per-project under `.bob/` instead.
|
|
28
|
+
|
|
29
|
+
## Status
|
|
30
|
+
|
|
31
|
+
| Priority | Owner | Status |
|
|
32
|
+
|----------|-------|--------|
|
|
33
|
+
| P0.1 foundation (diff + AST) | Neel | not started |
|
|
34
|
+
| P0.2 dead function detection | Jacob | not started |
|
|
35
|
+
| P0.3 vacuous test detection | Alexie | not started |
|
|
36
|
+
| P0.4 CLI + scorecard | Neel | not started |
|
|
37
|
+
| P1.1 execution tracer | Neel | not started |
|
|
38
|
+
| P1.2 MCP server | Ben | not started |
|
|
39
|
+
|
|
40
|
+
See `verdict-spec.md` and `TEAM.md` (local to the team, not in this repo) for the full plan.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: myverdict
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A lie detector for AI coding agents: audits diffs and traces what actually runs.
|
|
5
|
+
Author: verdict contributors
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/nshah271/verdict
|
|
8
|
+
Project-URL: Repository, https://github.com/nshah271/verdict
|
|
9
|
+
Project-URL: Issues, https://github.com/nshah271/verdict/issues
|
|
10
|
+
Keywords: ai,code-review,static-analysis,lie-detector,agents,claude,bob,mcp
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
14
|
+
Classifier: Topic :: Software Development :: Testing
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
21
|
+
Classifier: Operating System :: OS Independent
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
Requires-Dist: mcp>=1.0.0
|
|
25
|
+
Requires-Dist: click>=8.1
|
|
26
|
+
Requires-Dist: jedi>=0.19
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: ruff; extra == "dev"
|
|
29
|
+
Requires-Dist: pytest; extra == "dev"
|
|
30
|
+
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
31
|
+
Requires-Dist: pre-commit; extra == "dev"
|
|
32
|
+
|
|
33
|
+
# verdict
|
|
34
|
+
|
|
35
|
+
A lie detector for AI coding agents. Audits an AI-generated diff statically and traces what actually runs when the tests execute, then returns a single scorecard: `PASS`, `SUSPICIOUS`, or `LIED`.
|
|
36
|
+
|
|
37
|
+
Built for the IBM Bob hackathon (May 2026). Repo bootstrap in progress; the real README lands as part of P3.5.
|
|
38
|
+
|
|
39
|
+
## Install
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pip install myverdict
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
The PyPI package is named `myverdict` (the shorter `verdict-ai` slot was taken by an unrelated project before we shipped). The Python import is still `import verdict` and the CLI is still `verdict`.
|
|
46
|
+
|
|
47
|
+
## Quick start with IBM Bob
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# Install verdict's MCP server and Custom Mode globally so Bob sees them
|
|
51
|
+
# in every project on this machine:
|
|
52
|
+
verdict mcp-install --global
|
|
53
|
+
verdict bob-mode-install --global
|
|
54
|
+
|
|
55
|
+
# Restart Bob, then in any project switch to "Verifier" mode after a
|
|
56
|
+
# coding session or type /verify for a one-shot audit.
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Drop `--global` to install per-project under `.bob/` instead.
|
|
60
|
+
|
|
61
|
+
## Status
|
|
62
|
+
|
|
63
|
+
| Priority | Owner | Status |
|
|
64
|
+
|----------|-------|--------|
|
|
65
|
+
| P0.1 foundation (diff + AST) | Neel | not started |
|
|
66
|
+
| P0.2 dead function detection | Jacob | not started |
|
|
67
|
+
| P0.3 vacuous test detection | Alexie | not started |
|
|
68
|
+
| P0.4 CLI + scorecard | Neel | not started |
|
|
69
|
+
| P1.1 execution tracer | Neel | not started |
|
|
70
|
+
| P1.2 MCP server | Ben | not started |
|
|
71
|
+
|
|
72
|
+
See `verdict-spec.md` and `TEAM.md` (local to the team, not in this repo) for the full plan.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
myverdict.egg-info/PKG-INFO
|
|
4
|
+
myverdict.egg-info/SOURCES.txt
|
|
5
|
+
myverdict.egg-info/dependency_links.txt
|
|
6
|
+
myverdict.egg-info/entry_points.txt
|
|
7
|
+
myverdict.egg-info/requires.txt
|
|
8
|
+
myverdict.egg-info/top_level.txt
|
|
9
|
+
tests/test_ast_utils.py
|
|
10
|
+
tests/test_bob_integration.py
|
|
11
|
+
tests/test_bob_mode_install.py
|
|
12
|
+
tests/test_cli.py
|
|
13
|
+
tests/test_dead_functions.py
|
|
14
|
+
tests/test_diff.py
|
|
15
|
+
tests/test_hallucinated_api.py
|
|
16
|
+
tests/test_mcp_server.py
|
|
17
|
+
tests/test_report.py
|
|
18
|
+
tests/test_suppressed_exc.py
|
|
19
|
+
tests/test_trace.py
|
|
20
|
+
tests/test_vacuous_tests.py
|
|
21
|
+
verdict/__init__.py
|
|
22
|
+
verdict/_coverage_plugin.py
|
|
23
|
+
verdict/_tracer_plugin.py
|
|
24
|
+
verdict/ast_utils.py
|
|
25
|
+
verdict/cli.py
|
|
26
|
+
verdict/dashboard_cmd.py
|
|
27
|
+
verdict/diff.py
|
|
28
|
+
verdict/mcp_server.py
|
|
29
|
+
verdict/report.py
|
|
30
|
+
verdict/types.py
|
|
31
|
+
verdict/bob_integration/__init__.py
|
|
32
|
+
verdict/bob_integration/slash_commands/__init__.py
|
|
33
|
+
verdict/checks/__init__.py
|
|
34
|
+
verdict/checks/coverage_delta.py
|
|
35
|
+
verdict/checks/dead_functions.py
|
|
36
|
+
verdict/checks/hallucinated_api.py
|
|
37
|
+
verdict/checks/phantom_files.py
|
|
38
|
+
verdict/checks/suppressed_exc.py
|
|
39
|
+
verdict/checks/trace.py
|
|
40
|
+
verdict/checks/vacuous_tests.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "myverdict"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "A lie detector for AI coding agents: audits diffs and traces what actually runs."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "verdict contributors" }]
|
|
13
|
+
keywords = ["ai", "code-review", "static-analysis", "lie-detector", "agents", "claude", "bob", "mcp"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"Topic :: Software Development :: Quality Assurance",
|
|
18
|
+
"Topic :: Software Development :: Testing",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.10",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Programming Language :: Python :: 3.13",
|
|
24
|
+
"License :: OSI Approved :: MIT License",
|
|
25
|
+
"Operating System :: OS Independent",
|
|
26
|
+
]
|
|
27
|
+
dependencies = ["mcp>=1.0.0", "click>=8.1", "jedi>=0.19"]
|
|
28
|
+
|
|
29
|
+
[project.urls]
|
|
30
|
+
Homepage = "https://github.com/nshah271/verdict"
|
|
31
|
+
Repository = "https://github.com/nshah271/verdict"
|
|
32
|
+
Issues = "https://github.com/nshah271/verdict/issues"
|
|
33
|
+
|
|
34
|
+
[project.optional-dependencies]
|
|
35
|
+
dev = ["ruff", "pytest", "pytest-asyncio", "pre-commit"]
|
|
36
|
+
|
|
37
|
+
[project.scripts]
|
|
38
|
+
verdict = "verdict.cli:cli"
|
|
39
|
+
verdict-mcp = "verdict.mcp_server:entrypoint"
|
|
40
|
+
|
|
41
|
+
[tool.setuptools.packages.find]
|
|
42
|
+
include = ["verdict*"]
|
|
43
|
+
|
|
44
|
+
[tool.ruff]
|
|
45
|
+
line-length = 100
|
|
46
|
+
target-version = "py310"
|
|
47
|
+
|
|
48
|
+
[tool.ruff.lint]
|
|
49
|
+
select = ["E", "F", "I", "B", "UP"]
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
"""Tests for AST analysis functionality."""
|
|
2
|
+
|
|
3
|
+
import tempfile
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from verdict.ast_utils import get_added_functions, get_added_tests
|
|
7
|
+
from verdict.types import ChangedFile
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def test_get_added_functions_basic():
|
|
11
|
+
"""Test extracting added functions from a Python file."""
|
|
12
|
+
changed_file: ChangedFile = {
|
|
13
|
+
"path": "tests/fixtures/sample_module.py",
|
|
14
|
+
"added_lines": [8, 14, 21, 48, 51, 55], # regular_function, async_function, simple_fixture, SampleClass, __init__, method_one
|
|
15
|
+
"removed_lines": [],
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
added_functions = get_added_functions([changed_file], repo_root=".")
|
|
19
|
+
|
|
20
|
+
# Convert to dict for easier lookup
|
|
21
|
+
funcs_by_name = {f["name"]: f for f in added_functions}
|
|
22
|
+
|
|
23
|
+
# Check regular function
|
|
24
|
+
assert "regular_function" in funcs_by_name
|
|
25
|
+
regular = funcs_by_name["regular_function"]
|
|
26
|
+
assert regular["file"] == "tests/fixtures/sample_module.py"
|
|
27
|
+
assert regular["line"] == 8
|
|
28
|
+
assert regular["end_line"] == 10
|
|
29
|
+
assert regular["is_test"] is True # In tests/ directory
|
|
30
|
+
assert regular["decorators"] == []
|
|
31
|
+
|
|
32
|
+
# Check async function
|
|
33
|
+
assert "async_function" in funcs_by_name
|
|
34
|
+
async_func = funcs_by_name["async_function"]
|
|
35
|
+
assert async_func["line"] == 14
|
|
36
|
+
assert async_func["is_test"] is True # In tests/ directory
|
|
37
|
+
|
|
38
|
+
# Check function with decorator
|
|
39
|
+
assert "simple_fixture" in funcs_by_name
|
|
40
|
+
fixture = funcs_by_name["simple_fixture"]
|
|
41
|
+
assert fixture["line"] == 21
|
|
42
|
+
assert fixture["decorators"] == ["pytest.fixture"]
|
|
43
|
+
|
|
44
|
+
# Check class
|
|
45
|
+
assert "SampleClass" in funcs_by_name
|
|
46
|
+
cls = funcs_by_name["SampleClass"]
|
|
47
|
+
assert cls["line"] == 48
|
|
48
|
+
assert cls["is_test"] is True # In tests/ directory
|
|
49
|
+
|
|
50
|
+
# Check method
|
|
51
|
+
assert "method_one" in funcs_by_name
|
|
52
|
+
method = funcs_by_name["method_one"]
|
|
53
|
+
assert method["line"] == 55
|
|
54
|
+
assert method["is_test"] is True # In tests/ directory
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_decorator_with_call_args():
|
|
58
|
+
"""Test that decorators with call args are extracted correctly."""
|
|
59
|
+
changed_file: ChangedFile = {
|
|
60
|
+
"path": "tests/fixtures/sample_module.py",
|
|
61
|
+
"added_lines": [28], # complex_fixture function def line
|
|
62
|
+
"removed_lines": [],
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
added_functions = get_added_functions([changed_file], repo_root=".")
|
|
66
|
+
|
|
67
|
+
funcs_by_name = {f["name"]: f for f in added_functions}
|
|
68
|
+
assert "complex_fixture" in funcs_by_name
|
|
69
|
+
fixture = funcs_by_name["complex_fixture"]
|
|
70
|
+
# Should extract just the decorator name, not the call args
|
|
71
|
+
assert fixture["decorators"] == ["pytest.fixture"]
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def test_multiple_decorators():
|
|
75
|
+
"""Test function with multiple decorators."""
|
|
76
|
+
changed_file: ChangedFile = {
|
|
77
|
+
"path": "tests/fixtures/sample_module.py",
|
|
78
|
+
"added_lines": [36], # test_with_multiple_decorators function def line
|
|
79
|
+
"removed_lines": [],
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
added_functions = get_added_functions([changed_file], repo_root=".")
|
|
83
|
+
|
|
84
|
+
funcs_by_name = {f["name"]: f for f in added_functions}
|
|
85
|
+
assert "test_with_multiple_decorators" in funcs_by_name
|
|
86
|
+
test_func = funcs_by_name["test_with_multiple_decorators"]
|
|
87
|
+
assert test_func["is_test"] is True # name starts with test_
|
|
88
|
+
assert "pytest.mark.parametrize" in test_func["decorators"]
|
|
89
|
+
assert "pytest.mark.slow" in test_func["decorators"]
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def test_is_test_detection_by_name():
|
|
93
|
+
"""Test that functions starting with test_ are marked as tests."""
|
|
94
|
+
changed_file: ChangedFile = {
|
|
95
|
+
"path": "tests/fixtures/sample_module.py",
|
|
96
|
+
"added_lines": [42], # test_simple_case
|
|
97
|
+
"removed_lines": [],
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
added_functions = get_added_functions([changed_file], repo_root=".")
|
|
101
|
+
|
|
102
|
+
funcs_by_name = {f["name"]: f for f in added_functions}
|
|
103
|
+
assert "test_simple_case" in funcs_by_name
|
|
104
|
+
test_func = funcs_by_name["test_simple_case"]
|
|
105
|
+
assert test_func["is_test"] is True
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def test_is_test_detection_by_path():
|
|
109
|
+
"""Test that functions in test files are marked as tests."""
|
|
110
|
+
changed_file: ChangedFile = {
|
|
111
|
+
"path": "tests/fixtures/sample_module.py", # Contains "tests/"
|
|
112
|
+
"added_lines": [91], # helper_function (not named test_*)
|
|
113
|
+
"removed_lines": [],
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
added_functions = get_added_functions([changed_file], repo_root=".")
|
|
117
|
+
|
|
118
|
+
funcs_by_name = {f["name"]: f for f in added_functions}
|
|
119
|
+
assert "helper_function" in funcs_by_name
|
|
120
|
+
helper = funcs_by_name["helper_function"]
|
|
121
|
+
# Should be marked as test because path contains "tests/"
|
|
122
|
+
assert helper["is_test"] is True
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def test_class_and_methods_both_appear():
|
|
126
|
+
"""Test that both class and its methods appear as separate entries."""
|
|
127
|
+
changed_file: ChangedFile = {
|
|
128
|
+
"path": "tests/fixtures/sample_module.py",
|
|
129
|
+
"added_lines": [48, 51, 55, 59], # SampleClass, __init__, method_one, async_method
|
|
130
|
+
"removed_lines": [],
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
added_functions = get_added_functions([changed_file], repo_root=".")
|
|
134
|
+
|
|
135
|
+
funcs_by_name = {f["name"]: f for f in added_functions}
|
|
136
|
+
|
|
137
|
+
# Class should appear
|
|
138
|
+
assert "SampleClass" in funcs_by_name
|
|
139
|
+
|
|
140
|
+
# Methods should also appear as separate entries
|
|
141
|
+
assert "__init__" in funcs_by_name
|
|
142
|
+
assert "method_one" in funcs_by_name
|
|
143
|
+
assert "async_method" in funcs_by_name
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def test_async_function_detected():
|
|
147
|
+
"""Test that async functions are properly detected."""
|
|
148
|
+
changed_file: ChangedFile = {
|
|
149
|
+
"path": "tests/fixtures/sample_module.py",
|
|
150
|
+
"added_lines": [14, 59, 84], # async_function, async_method, test_async_with_decorators
|
|
151
|
+
"removed_lines": [],
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
added_functions = get_added_functions([changed_file], repo_root=".")
|
|
155
|
+
|
|
156
|
+
funcs_by_name = {f["name"]: f for f in added_functions}
|
|
157
|
+
|
|
158
|
+
# All three async functions should be detected
|
|
159
|
+
assert "async_function" in funcs_by_name
|
|
160
|
+
assert "async_method" in funcs_by_name
|
|
161
|
+
assert "test_async_with_decorators" in funcs_by_name
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def test_syntax_error_skipped():
|
|
165
|
+
"""Test that files with syntax errors are skipped without crashing."""
|
|
166
|
+
# Create a temporary file with invalid Python syntax
|
|
167
|
+
with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
|
|
168
|
+
f.write("def broken_function(\n # Missing closing paren and body\n")
|
|
169
|
+
temp_path = f.name
|
|
170
|
+
|
|
171
|
+
try:
|
|
172
|
+
changed_file: ChangedFile = {
|
|
173
|
+
"path": temp_path,
|
|
174
|
+
"added_lines": [1],
|
|
175
|
+
"removed_lines": [],
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
# Should not crash, just skip the file
|
|
179
|
+
added_functions = get_added_functions([changed_file], repo_root=".")
|
|
180
|
+
assert added_functions == []
|
|
181
|
+
finally:
|
|
182
|
+
Path(temp_path).unlink()
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def test_non_python_file_skipped():
|
|
186
|
+
"""Test that non-Python files are skipped."""
|
|
187
|
+
changed_file: ChangedFile = {
|
|
188
|
+
"path": "README.md",
|
|
189
|
+
"added_lines": [1, 2, 3],
|
|
190
|
+
"removed_lines": [],
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
added_functions = get_added_functions([changed_file], repo_root=".")
|
|
194
|
+
assert added_functions == []
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def test_empty_added_lines():
|
|
198
|
+
"""Test that files with no added lines return no functions."""
|
|
199
|
+
changed_file: ChangedFile = {
|
|
200
|
+
"path": "tests/fixtures/sample_module.py",
|
|
201
|
+
"added_lines": [], # No added lines
|
|
202
|
+
"removed_lines": [1, 2, 3],
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
added_functions = get_added_functions([changed_file], repo_root=".")
|
|
206
|
+
assert added_functions == []
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def test_get_added_tests_filter():
|
|
210
|
+
"""Test that get_added_tests correctly filters test functions."""
|
|
211
|
+
changed_file: ChangedFile = {
|
|
212
|
+
"path": "tests/fixtures/sample_module.py",
|
|
213
|
+
"added_lines": [8, 42, 91], # regular_function, test_simple_case, helper_function
|
|
214
|
+
"removed_lines": [],
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
added_functions = get_added_functions([changed_file], repo_root=".")
|
|
218
|
+
test_functions = get_added_tests(added_functions)
|
|
219
|
+
|
|
220
|
+
# Should only include test functions
|
|
221
|
+
test_names = {f["name"] for f in test_functions}
|
|
222
|
+
assert "test_simple_case" in test_names
|
|
223
|
+
assert "helper_function" in test_names # In tests/ directory
|
|
224
|
+
assert "regular_function" in test_names # Also a test because in tests/ directory
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def test_nested_class():
|
|
228
|
+
"""Test that nested classes are detected."""
|
|
229
|
+
changed_file: ChangedFile = {
|
|
230
|
+
"path": "tests/fixtures/sample_module.py",
|
|
231
|
+
"added_lines": [70, 73, 76], # OuterClass, InnerClass, inner_method
|
|
232
|
+
"removed_lines": [],
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
added_functions = get_added_functions([changed_file], repo_root=".")
|
|
236
|
+
|
|
237
|
+
funcs_by_name = {f["name"]: f for f in added_functions}
|
|
238
|
+
|
|
239
|
+
# Both outer and inner classes should appear
|
|
240
|
+
assert "OuterClass" in funcs_by_name
|
|
241
|
+
assert "InnerClass" in funcs_by_name
|
|
242
|
+
assert "inner_method" in funcs_by_name
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def test_property_decorator():
|
|
246
|
+
"""Test that property decorators are extracted correctly."""
|
|
247
|
+
changed_file: ChangedFile = {
|
|
248
|
+
"path": "tests/fixtures/sample_module.py",
|
|
249
|
+
"added_lines": [64], # computed_property with @property
|
|
250
|
+
"removed_lines": [],
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
added_functions = get_added_functions([changed_file], repo_root=".")
|
|
254
|
+
|
|
255
|
+
funcs_by_name = {f["name"]: f for f in added_functions}
|
|
256
|
+
assert "computed_property" in funcs_by_name
|
|
257
|
+
prop = funcs_by_name["computed_property"]
|
|
258
|
+
assert "property" in prop["decorators"]
|
|
259
|
+
|
|
260
|
+
# Made with Bob
|