Public Access
Post-merge / detect-type (push) Successful in 9s
Post-merge / validate-commit-msg (push) Successful in 9s
Build Images / detect-type (push) Successful in 42s
Post-merge / vikunja (push) Successful in 17s
Post-merge / release (push) Successful in 52s
Post-merge / configure-repo (push) Successful in 23s
Post-merge / badges (push) Successful in 55s
Post-merge / sync-wiki (push) Successful in 58s
Post-merge / publish (push) Successful in 21s
Build Images / build-and-push (push) Successful in 3m15s
Build Images / cleanup (push) Successful in 3m38s
177 lines
7.6 KiB
Python
177 lines
7.6 KiB
Python
"""Unit tests for devx.tools.check_api_identity_checks."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
from unittest.mock import patch
|
|
|
|
from click.testing import CliRunner
|
|
|
|
from devx.tools.check_api_identity_checks import (
|
|
DEFAULT_NOQA_MARKER,
|
|
DEFAULT_SCAN_DIRS,
|
|
DEFAULT_SKIP_PATTERNS,
|
|
_load_config,
|
|
_matches_skip_pattern,
|
|
cli,
|
|
find_identity_checks,
|
|
)
|
|
|
|
|
|
class TestFindIdentityChecks:
|
|
def test_detects_is_true(self, tmp_path: Path) -> None:
|
|
f = tmp_path / "test_foo.py"
|
|
f.write_text("assert config.get('x') is True\n")
|
|
issues = find_identity_checks(f, tmp_path, DEFAULT_NOQA_MARKER)
|
|
assert len(issues) == 1
|
|
assert "is True" in issues[0]
|
|
|
|
def test_detects_is_false(self, tmp_path: Path) -> None:
|
|
f = tmp_path / "test_foo.py"
|
|
f.write_text("if config.get('x') is False:\n pass\n")
|
|
issues = find_identity_checks(f, tmp_path, DEFAULT_NOQA_MARKER)
|
|
assert len(issues) == 1
|
|
assert "is False" in issues[0]
|
|
|
|
def test_detects_is_not_true(self, tmp_path: Path) -> None:
|
|
f = tmp_path / "test_foo.py"
|
|
f.write_text("if config.get('x') is not True:\n fail()\n")
|
|
issues = find_identity_checks(f, tmp_path, DEFAULT_NOQA_MARKER)
|
|
assert len(issues) == 1
|
|
assert "is not True" in issues[0]
|
|
|
|
def test_detects_is_not_false(self, tmp_path: Path) -> None:
|
|
f = tmp_path / "test_foo.py"
|
|
f.write_text("if config.get('x') is not False:\n fail()\n")
|
|
issues = find_identity_checks(f, tmp_path, DEFAULT_NOQA_MARKER)
|
|
assert len(issues) == 1
|
|
assert "is not False" in issues[0]
|
|
|
|
def test_noqa_suppresses(self, tmp_path: Path) -> None:
|
|
f = tmp_path / "test_foo.py"
|
|
f.write_text("assert config.get('x') is True # noqa\n")
|
|
issues = find_identity_checks(f, tmp_path, DEFAULT_NOQA_MARKER)
|
|
assert len(issues) == 0
|
|
|
|
def test_no_false_positives(self, tmp_path: Path) -> None:
|
|
f = tmp_path / "test_foo.py"
|
|
f.write_text("assert config.get('x') == 'true'\nassert config.get('y') == True\nx = True\nif x:\n pass\n")
|
|
issues = find_identity_checks(f, tmp_path, DEFAULT_NOQA_MARKER)
|
|
assert len(issues) == 0
|
|
|
|
def test_multiple_issues(self, tmp_path: Path) -> None:
|
|
f = tmp_path / "test_foo.py"
|
|
f.write_text("if config.get('a') is True:\n pass\nif config.get('b') is not False:\n pass\n")
|
|
issues = find_identity_checks(f, tmp_path, DEFAULT_NOQA_MARKER)
|
|
assert len(issues) == 2
|
|
|
|
def test_file_not_found(self, tmp_path: Path) -> None:
|
|
f = tmp_path / "nonexistent.py"
|
|
issues = find_identity_checks(f, tmp_path, DEFAULT_NOQA_MARKER)
|
|
assert issues == []
|
|
|
|
|
|
class TestMatchesSkipPattern:
|
|
def test_matches_helpers(self) -> None:
|
|
assert _matches_skip_pattern(Path("test_mattermost_helpers.py"), DEFAULT_SKIP_PATTERNS)
|
|
|
|
def test_does_not_match_regular(self) -> None:
|
|
assert not _matches_skip_pattern(Path("test_mattermost.py"), DEFAULT_SKIP_PATTERNS)
|
|
|
|
def test_empty_patterns(self) -> None:
|
|
assert not _matches_skip_pattern(Path("test_anything.py"), [])
|
|
|
|
|
|
class TestLoadConfig:
|
|
def test_defaults(self) -> None:
|
|
with patch("devx.tools.check_api_identity_checks._load_pyproject_devx") as mock:
|
|
mock.return_value = {}
|
|
scan_dirs, skip_patterns, noqa = _load_config()
|
|
assert scan_dirs == DEFAULT_SCAN_DIRS
|
|
assert skip_patterns == DEFAULT_SKIP_PATTERNS
|
|
assert noqa == DEFAULT_NOQA_MARKER
|
|
|
|
def test_custom_config(self) -> None:
|
|
with patch("devx.tools.check_api_identity_checks._load_pyproject_devx") as mock:
|
|
mock.return_value = {
|
|
"check_api_identity_checks": {
|
|
"scan_dirs": ["tests/api"],
|
|
"skip_patterns": ["test_*_unit.py"],
|
|
"noqa_marker": "# allow",
|
|
}
|
|
}
|
|
scan_dirs, skip_patterns, noqa = _load_config()
|
|
assert scan_dirs == ["tests/api"]
|
|
assert skip_patterns == ["test_*_unit.py"]
|
|
assert noqa == "# allow"
|
|
|
|
def test_invalid_config_returns_defaults(self) -> None:
|
|
with patch("devx.tools.check_api_identity_checks._load_pyproject_devx") as mock:
|
|
mock.return_value = {"check_api_identity_checks": "not a dict"}
|
|
scan_dirs, _, _ = _load_config()
|
|
assert scan_dirs == DEFAULT_SCAN_DIRS
|
|
|
|
|
|
class TestCli:
|
|
def test_no_issues(self, tmp_path: Path) -> None:
|
|
runner = CliRunner()
|
|
with (
|
|
patch("devx.tools.check_api_identity_checks._load_config") as mock_cfg,
|
|
patch("devx.tools.check_api_identity_checks.Path.cwd", return_value=tmp_path),
|
|
):
|
|
mock_cfg.return_value = (["tests/integration"], DEFAULT_SKIP_PATTERNS, DEFAULT_NOQA_MARKER)
|
|
(tmp_path / "tests" / "integration").mkdir(parents=True)
|
|
(tmp_path / "tests" / "integration" / "test_foo.py").write_text("assert config.get('x') == 'true'\n")
|
|
result = runner.invoke(cli, [])
|
|
assert result.exit_code == 0
|
|
assert "Passed" in result.output
|
|
|
|
def test_with_issues(self, tmp_path: Path) -> None:
|
|
runner = CliRunner()
|
|
with (
|
|
patch("devx.tools.check_api_identity_checks._load_config") as mock_cfg,
|
|
patch("devx.tools.check_api_identity_checks.Path.cwd", return_value=tmp_path),
|
|
):
|
|
mock_cfg.return_value = (["tests/integration"], DEFAULT_SKIP_PATTERNS, DEFAULT_NOQA_MARKER)
|
|
(tmp_path / "tests" / "integration").mkdir(parents=True)
|
|
(tmp_path / "tests" / "integration" / "test_foo.py").write_text(
|
|
"if config.get('x') is not True:\n fail()\n"
|
|
)
|
|
result = runner.invoke(cli, [])
|
|
assert result.exit_code != 0
|
|
assert "is not True" in result.output
|
|
|
|
def test_skips_helpers(self, tmp_path: Path) -> None:
|
|
runner = CliRunner()
|
|
with (
|
|
patch("devx.tools.check_api_identity_checks._load_config") as mock_cfg,
|
|
patch("devx.tools.check_api_identity_checks.Path.cwd", return_value=tmp_path),
|
|
):
|
|
mock_cfg.return_value = (["tests/integration"], DEFAULT_SKIP_PATTERNS, DEFAULT_NOQA_MARKER)
|
|
(tmp_path / "tests" / "integration").mkdir(parents=True)
|
|
(tmp_path / "tests" / "integration" / "test_foo_helpers.py").write_text("assert x is True\n")
|
|
result = runner.invoke(cli, [])
|
|
assert result.exit_code == 0
|
|
|
|
def test_nonexistent_dir(self, tmp_path: Path) -> None:
|
|
runner = CliRunner()
|
|
with (
|
|
patch("devx.tools.check_api_identity_checks._load_config") as mock_cfg,
|
|
patch("devx.tools.check_api_identity_checks.Path.cwd", return_value=tmp_path),
|
|
):
|
|
mock_cfg.return_value = (["nonexistent"], DEFAULT_SKIP_PATTERNS, DEFAULT_NOQA_MARKER)
|
|
result = runner.invoke(cli, [])
|
|
assert result.exit_code == 0
|
|
|
|
def test_custom_scan_dir(self, tmp_path: Path) -> None:
|
|
runner = CliRunner()
|
|
with (
|
|
patch("devx.tools.check_api_identity_checks._load_config") as mock_cfg,
|
|
patch("devx.tools.check_api_identity_checks.Path.cwd", return_value=tmp_path),
|
|
):
|
|
mock_cfg.return_value = (["other"], DEFAULT_SKIP_PATTERNS, DEFAULT_NOQA_MARKER)
|
|
(tmp_path / "custom").mkdir()
|
|
(tmp_path / "custom" / "test_foo.py").write_text("if x is True:\n pass\n")
|
|
result = runner.invoke(cli, ["--scan-dir", "custom"])
|
|
assert result.exit_code != 0
|