122af46305
- Fix project_repository_steps session mismatch: use shared session for repos so context.pr_session.commit() commits flushed data - Fix cli_format_bench setup/teardown to accept fmt parameter for parameterized ASV benchmarks - Fix plan_model_bench PlanPhase.APPLIED -> PlanPhase.APPLY - Fix benchmark idempotency: use batch counters to generate unique IDs across repeated ASV iterations (plan_phase_migration_bench, project_migration_bench, resource_registry_migration_bench) - Fix uow_lifecycle_bench FK constraint by reusing pre-seeded action - Fix plan_lifecycle_persistence_bench to create fresh plan per iteration
179 lines
5.7 KiB
Python
179 lines
5.7 KiB
Python
"""Airspeed Velocity benchmarks for v3 Plan domain model.
|
|
|
|
Measures validation, serialization, and as_cli_dict performance for the
|
|
Plan model across different field configurations.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from cleveragents.domain.models.core.plan import (
|
|
AutomationProfileProvenance,
|
|
AutomationProfileRef,
|
|
InvariantSource,
|
|
NamespacedName,
|
|
Plan,
|
|
PlanIdentity,
|
|
PlanInvariant,
|
|
PlanPhase,
|
|
ProcessingState,
|
|
ProjectLink,
|
|
SubplanConfig,
|
|
SubplanStatus,
|
|
can_transition,
|
|
)
|
|
|
|
# Valid ULID constants for benchmarks
|
|
_ULID_A = "01HGZ6FE0AQDYTR4BXVQZ6EA00"
|
|
_ULID_B = "01HGZ6FE0AQDYTR4BXVQZ6EB00"
|
|
|
|
|
|
def _minimal_plan() -> Plan:
|
|
"""Create a minimal Plan with only required fields."""
|
|
return Plan(
|
|
identity=PlanIdentity(plan_id=_ULID_A),
|
|
namespaced_name=NamespacedName(
|
|
server=None, namespace="local", name="bench-plan"
|
|
),
|
|
description="Benchmark plan",
|
|
action_name="local/bench-action",
|
|
)
|
|
|
|
|
|
def _rich_plan() -> Plan:
|
|
"""Create a Plan with all optional fields populated."""
|
|
return Plan(
|
|
identity=PlanIdentity(
|
|
plan_id=_ULID_A,
|
|
parent_plan_id=_ULID_B,
|
|
root_plan_id=_ULID_B,
|
|
),
|
|
namespaced_name=NamespacedName(
|
|
server="prod", namespace="myorg", name="rich-plan"
|
|
),
|
|
description="Fully populated benchmark plan",
|
|
definition_of_done="All tests pass with >97% coverage",
|
|
action_name="myorg/code-coverage",
|
|
phase=PlanPhase.EXECUTE,
|
|
processing_state=ProcessingState.PROCESSING,
|
|
automation_profile=AutomationProfileRef(
|
|
profile_name="trusted",
|
|
provenance=AutomationProfileProvenance.ACTION,
|
|
),
|
|
project_links=[
|
|
ProjectLink(project_name="local/api-svc", alias="api", read_only=False),
|
|
ProjectLink(project_name="local/docs", read_only=True),
|
|
],
|
|
invariants=[
|
|
PlanInvariant(text="No public API changes", source=InvariantSource.ACTION),
|
|
PlanInvariant(text="Keep backward compat", source=InvariantSource.PROJECT),
|
|
],
|
|
arguments={"target": "src/main.py", "mode": "strict", "verbose": True},
|
|
arguments_order=["target", "mode", "verbose"],
|
|
strategy_actor="local/planner",
|
|
execution_actor="local/coder",
|
|
review_actor="local/reviewer",
|
|
error_message=None,
|
|
subplan_config=SubplanConfig(max_parallel=3),
|
|
subplan_statuses=[
|
|
SubplanStatus(subplan_id=_ULID_B, action_name="local/sub-action"),
|
|
],
|
|
)
|
|
|
|
|
|
class PlanValidationSuite:
|
|
"""Benchmark Plan model validation (construction)."""
|
|
|
|
def time_minimal_plan_creation(self) -> None:
|
|
"""Time creating a minimal Plan with only required fields."""
|
|
_minimal_plan()
|
|
|
|
def time_rich_plan_creation(self) -> None:
|
|
"""Time creating a Plan with all optional fields populated."""
|
|
_rich_plan()
|
|
|
|
def time_plan_model_validate_dict(self) -> None:
|
|
"""Time Plan.model_validate from a raw dict."""
|
|
Plan.model_validate(
|
|
{
|
|
"identity": {"plan_id": _ULID_A},
|
|
"namespaced_name": {"namespace": "local", "name": "bench-plan"},
|
|
"description": "Benchmark plan",
|
|
"action_name": "local/bench-action",
|
|
}
|
|
)
|
|
|
|
|
|
class PlanSerializationSuite:
|
|
"""Benchmark Plan model serialization."""
|
|
|
|
def setup(self) -> None:
|
|
self.minimal = _minimal_plan()
|
|
self.rich = _rich_plan()
|
|
|
|
def time_minimal_model_dump(self) -> None:
|
|
"""Time model_dump for a minimal Plan."""
|
|
self.minimal.model_dump()
|
|
|
|
def time_rich_model_dump(self) -> None:
|
|
"""Time model_dump for a fully populated Plan."""
|
|
self.rich.model_dump()
|
|
|
|
def time_minimal_model_dump_json(self) -> None:
|
|
"""Time model_dump_json for a minimal Plan."""
|
|
self.minimal.model_dump_json()
|
|
|
|
def time_rich_model_dump_json(self) -> None:
|
|
"""Time model_dump_json for a fully populated Plan."""
|
|
self.rich.model_dump_json()
|
|
|
|
|
|
class PlanCliDictSuite:
|
|
"""Benchmark Plan.as_cli_dict output generation."""
|
|
|
|
def setup(self) -> None:
|
|
self.minimal = _minimal_plan()
|
|
self.rich = _rich_plan()
|
|
|
|
def time_minimal_as_cli_dict(self) -> None:
|
|
"""Time as_cli_dict for a minimal Plan."""
|
|
self.minimal.as_cli_dict()
|
|
|
|
def time_rich_as_cli_dict(self) -> None:
|
|
"""Time as_cli_dict for a Plan with all optional fields."""
|
|
self.rich.as_cli_dict()
|
|
|
|
|
|
class PhaseTransitionSuite:
|
|
"""Benchmark phase transition validation."""
|
|
|
|
def time_can_transition_valid(self) -> None:
|
|
"""Time a valid phase transition check."""
|
|
can_transition(PlanPhase.STRATEGIZE, PlanPhase.EXECUTE)
|
|
|
|
def time_can_transition_invalid(self) -> None:
|
|
"""Time an invalid phase transition check."""
|
|
can_transition(PlanPhase.STRATEGIZE, PlanPhase.APPLY)
|
|
|
|
def time_all_transitions(self) -> None:
|
|
"""Time checking all possible phase transition pairs."""
|
|
for from_phase in PlanPhase:
|
|
for to_phase in PlanPhase:
|
|
can_transition(from_phase, to_phase)
|
|
|
|
|
|
class NamespacedNameSuite:
|
|
"""Benchmark NamespacedName parsing and serialization."""
|
|
|
|
def time_parse_simple(self) -> None:
|
|
"""Time parsing a simple name."""
|
|
NamespacedName.parse("my-action")
|
|
|
|
def time_parse_full(self) -> None:
|
|
"""Time parsing a full server:namespace/name string."""
|
|
NamespacedName.parse("prod:myorg/my-action")
|
|
|
|
def time_str_representation(self) -> None:
|
|
"""Time string representation of a NamespacedName."""
|
|
name = NamespacedName(server="prod", namespace="myorg", name="my-action")
|
|
str(name)
|