ace7311de4
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 16s
CI / quality (pull_request) Successful in 19s
CI / build (pull_request) Successful in 20s
CI / security (pull_request) Successful in 55s
CI / typecheck (pull_request) Successful in 1m5s
CI / integration_tests (pull_request) Successful in 4m4s
CI / benchmark-regression (pull_request) Successful in 22m21s
CI / unit_tests (pull_request) Failing after 34m7s
CI / docker (pull_request) Has been skipped
CI / coverage (pull_request) Has been cancelled
256 lines
8.0 KiB
Python
256 lines
8.0 KiB
Python
"""ASV benchmarks for M3 decision tree, validation, and invariant CLI overhead.
|
|
|
|
Measures the performance of:
|
|
- Invariant add / list / remove CLI commands
|
|
- Validation add / attach / detach CLI commands
|
|
- Plan correct (dry-run) CLI command
|
|
- Decision tree fixture loading
|
|
- Invariant merge precedence computation
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import importlib
|
|
import json
|
|
import sys
|
|
import tempfile
|
|
from pathlib import Path
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
# Ensure the local *source* tree is importable even when ASV has an
|
|
# older build of the package installed.
|
|
_SRC = str(Path(__file__).resolve().parents[1] / "src")
|
|
if _SRC not in sys.path:
|
|
sys.path.insert(0, _SRC)
|
|
|
|
import cleveragents # noqa: E402
|
|
|
|
importlib.reload(cleveragents)
|
|
|
|
from typer.testing import CliRunner # noqa: E402
|
|
|
|
from cleveragents.cli.commands.invariant import app as invariant_app # noqa: E402
|
|
from cleveragents.cli.commands.plan import app as plan_app # noqa: E402
|
|
from cleveragents.cli.commands.validation import app as validation_app # noqa: E402
|
|
from cleveragents.domain.models.core.correction import ( # noqa: E402
|
|
CorrectionMode,
|
|
CorrectionStatus,
|
|
)
|
|
from cleveragents.domain.models.core.invariant import ( # noqa: E402
|
|
Invariant,
|
|
InvariantScope,
|
|
)
|
|
|
|
_runner = CliRunner()
|
|
_PLAN_ULID = "01M3SM0KE00000000000000001"
|
|
_DECISION_ULID = "01M3DEC1S10N0000000000001"
|
|
_CORRECTION_ULID = "01M3C0RRECT10N00000000001"
|
|
_ATTACHMENT_ULID = "01M3ATTACH0000000000000001"
|
|
_INVARIANT_ULID = "01M3INVAR1ANT0000000000001"
|
|
_FIXTURES_DIR = Path(__file__).resolve().parents[1] / "features" / "fixtures" / "m3"
|
|
|
|
|
|
def _mock_invariant(
|
|
*,
|
|
text: str = "Never delete production data",
|
|
scope: InvariantScope = InvariantScope.GLOBAL,
|
|
source_name: str = "system",
|
|
) -> Invariant:
|
|
return Invariant(
|
|
id=_INVARIANT_ULID,
|
|
text=text,
|
|
scope=scope,
|
|
source_name=source_name,
|
|
)
|
|
|
|
|
|
class M3InvariantAddSuite:
|
|
"""Benchmark invariant add CLI command."""
|
|
|
|
def setup(self) -> None:
|
|
self._mock_service = MagicMock()
|
|
self._mock_service.add_invariant.return_value = _mock_invariant()
|
|
self._patcher = patch(
|
|
"cleveragents.cli.commands.invariant._get_service",
|
|
return_value=self._mock_service,
|
|
)
|
|
self._patcher.start()
|
|
|
|
def teardown(self) -> None:
|
|
self._patcher.stop()
|
|
|
|
def time_invariant_add_global(self) -> None:
|
|
"""Benchmark invariant add with global scope."""
|
|
_runner.invoke(
|
|
invariant_app,
|
|
["add", "--global", "Never delete production data", "--format", "plain"],
|
|
)
|
|
|
|
def time_invariant_add_project(self) -> None:
|
|
"""Benchmark invariant add with project scope."""
|
|
_runner.invoke(
|
|
invariant_app,
|
|
[
|
|
"add",
|
|
"--project",
|
|
"local/m3-smoke-proj",
|
|
"All API changes need tests",
|
|
"--format",
|
|
"plain",
|
|
],
|
|
)
|
|
|
|
|
|
class M3InvariantListSuite:
|
|
"""Benchmark invariant list CLI command."""
|
|
|
|
def setup(self) -> None:
|
|
self._mock_service = MagicMock()
|
|
self._mock_service.list_invariants.return_value = [
|
|
_mock_invariant(),
|
|
_mock_invariant(
|
|
text="All API changes need tests",
|
|
scope=InvariantScope.PROJECT,
|
|
source_name="local/m3-smoke-proj",
|
|
),
|
|
]
|
|
self._patcher = patch(
|
|
"cleveragents.cli.commands.invariant._get_service",
|
|
return_value=self._mock_service,
|
|
)
|
|
self._patcher.start()
|
|
|
|
def teardown(self) -> None:
|
|
self._patcher.stop()
|
|
|
|
def time_invariant_list(self) -> None:
|
|
"""Benchmark invariant list command."""
|
|
_runner.invoke(invariant_app, ["list", "--format", "plain"])
|
|
|
|
def time_invariant_list_with_project(self) -> None:
|
|
"""Benchmark invariant list with project filter."""
|
|
_runner.invoke(
|
|
invariant_app,
|
|
["list", "--project", "local/m3-smoke-proj", "--format", "plain"],
|
|
)
|
|
|
|
|
|
class M3ValidationAddSuite:
|
|
"""Benchmark validation add CLI command."""
|
|
|
|
def setup(self) -> None:
|
|
self._mock_service = MagicMock()
|
|
mock_validation = MagicMock()
|
|
mock_validation.name = "local/coverage-check"
|
|
mock_validation.as_cli_dict.return_value = {
|
|
"name": "local/coverage-check",
|
|
"description": "Check code coverage meets threshold",
|
|
"source": "custom",
|
|
"mode": "required",
|
|
}
|
|
self._mock_service.register_tool.return_value = mock_validation
|
|
self._patcher = patch(
|
|
"cleveragents.cli.commands.validation._get_tool_registry_service",
|
|
return_value=self._mock_service,
|
|
)
|
|
self._patcher.start()
|
|
|
|
config_content = (
|
|
"name: local/coverage-check\n"
|
|
"description: Check code coverage meets threshold\n"
|
|
"source: custom\n"
|
|
"mode: required\n"
|
|
"code: |\n"
|
|
" def run(inputs):\n"
|
|
" return {'passed': inputs['coverage'] >= 80}\n"
|
|
)
|
|
with tempfile.NamedTemporaryFile(
|
|
mode="w",
|
|
suffix=".yaml",
|
|
delete=False,
|
|
) as tmp:
|
|
tmp.write(config_content)
|
|
self._config_path = tmp.name
|
|
|
|
def teardown(self) -> None:
|
|
self._patcher.stop()
|
|
|
|
def time_validation_add(self) -> None:
|
|
"""Benchmark validation add from YAML config."""
|
|
_runner.invoke(
|
|
validation_app,
|
|
["add", "--config", self._config_path, "--format", "plain"],
|
|
)
|
|
|
|
|
|
class M3PlanCorrectSuite:
|
|
"""Benchmark plan correct (dry-run) CLI command."""
|
|
|
|
def setup(self) -> None:
|
|
mock_correction_svc = MagicMock()
|
|
mock_request = MagicMock()
|
|
mock_request.correction_id = _CORRECTION_ULID
|
|
mock_request.mode = CorrectionMode.REVERT
|
|
mock_request.target_decision_id = _DECISION_ULID
|
|
mock_request.guidance = "Use FastAPI instead"
|
|
mock_request.status = CorrectionStatus.PENDING
|
|
|
|
mock_impact = MagicMock()
|
|
mock_impact.affected_decisions = [_DECISION_ULID]
|
|
mock_impact.affected_files = ["src/main.py"]
|
|
mock_impact.estimated_cost = "low"
|
|
mock_impact.risk_level = "low"
|
|
|
|
mock_correction_svc.request_correction.return_value = mock_request
|
|
mock_correction_svc.analyze_impact.return_value = mock_impact
|
|
|
|
self._patcher = patch(
|
|
"cleveragents.application.services.correction_service.CorrectionService",
|
|
return_value=mock_correction_svc,
|
|
)
|
|
self._patcher.start()
|
|
|
|
def teardown(self) -> None:
|
|
self._patcher.stop()
|
|
|
|
def time_plan_correct_dry_run(self) -> None:
|
|
"""Benchmark plan correct dry-run command."""
|
|
_runner.invoke(
|
|
plan_app,
|
|
[
|
|
"correct",
|
|
_DECISION_ULID,
|
|
"--mode",
|
|
"revert",
|
|
"--guidance",
|
|
"Use FastAPI instead",
|
|
"--dry-run",
|
|
"--plan",
|
|
_PLAN_ULID,
|
|
"--format",
|
|
"plain",
|
|
],
|
|
)
|
|
|
|
|
|
class M3FixtureLoadSuite:
|
|
"""Benchmark loading M3 fixture files."""
|
|
|
|
def time_load_decision_tree_fixture(self) -> None:
|
|
"""Benchmark loading decision_tree_outputs.json fixture."""
|
|
fixture_path = _FIXTURES_DIR / "decision_tree_outputs.json"
|
|
with open(fixture_path) as f:
|
|
json.load(f)
|
|
|
|
def time_load_validation_attachments_fixture(self) -> None:
|
|
"""Benchmark loading validation_attachments.json fixture."""
|
|
fixture_path = _FIXTURES_DIR / "validation_attachments.json"
|
|
with open(fixture_path) as f:
|
|
json.load(f)
|
|
|
|
def time_load_invariant_configs_fixture(self) -> None:
|
|
"""Benchmark loading invariant_configs.json fixture."""
|
|
fixture_path = _FIXTURES_DIR / "invariant_configs.json"
|
|
with open(fixture_path) as f:
|
|
json.load(f)
|