80f1664a0d
CI / benchmark-publish (pull_request) Has been skipped
CI / build (pull_request) Successful in 23s
CI / lint (pull_request) Successful in 3m20s
CI / typecheck (pull_request) Successful in 3m54s
CI / security (pull_request) Successful in 4m13s
CI / quality (pull_request) Successful in 4m17s
CI / integration_tests (pull_request) Successful in 9m21s
CI / unit_tests (pull_request) Successful in 9m42s
CI / docker (pull_request) Successful in 14s
CI / e2e_tests (pull_request) Successful in 12m1s
CI / coverage (pull_request) Successful in 11m41s
CI / status-check (pull_request) Successful in 1s
CI / build (push) Successful in 26s
CI / lint (push) Successful in 3m19s
CI / quality (push) Successful in 3m41s
CI / typecheck (push) Successful in 3m56s
CI / benchmark-regression (push) Has been skipped
CI / security (push) Successful in 4m2s
CI / integration_tests (push) Successful in 9m3s
CI / unit_tests (push) Successful in 9m24s
CI / docker (push) Successful in 1m9s
CI / e2e_tests (push) Successful in 13m51s
CI / coverage (push) Successful in 11m25s
CI / status-check (push) Successful in 1s
CI / benchmark-publish (push) Failing after 24m52s
CI / benchmark-regression (pull_request) Failing after 37m6s
Implemented Robot Framework integration test suite validating the CI/CD workflow (Specification Example 7). Tests cover ci-profile configuration (automation-profile, format, log level), idempotent resource and project registration with duplicate-detection assertions, three validation tools (ci-lint, ci-typecheck, ci-tests) registration and resource attachment via ToolRegistryService, action creation with typed arguments and invariants per spec Step 2, plan lifecycle with phase-by-phase completion through all phases (strategize, execute, apply) until terminal applied state, and JSON output structure verification including plan_id, phase, state, action, projects, and arguments fields. Post-review fixes applied (round 1): - H1: Fix truncated invariant #2 text to match spec line 39051 - H2: Fix definition_of_done to match spec's 5-bullet-point format - M1: Register all 3 spec validations (ci-lint, ci-typecheck, ci-tests) - M3: Add branch property to resource registration per spec Step 3 - M4: Replace phantom resource_id with real registered resource - M5: Add projects and arguments field assertions to JSON output test - M7: Replace 'polling-based' with 'phase-by-phase' in docs/comments - L1: Use timezone-aware datetime (timezone.utc) - L2: Use tempfile for resource path instead of hardcoded /tmp - L3: Clarify json_output() docstring to reflect as_cli_dict() scope - L4: Tighten attachment count assertion from >= 1 to == 1 (now == 3) Post-review fixes applied (round 2): - M2: Use Setup Test Environment With Database Isolation for pabot safety - L1: Replace remaining hardcoded /tmp paths with tempfile (validation_attach, resource_idempotent duplicate registration) - L2: Fix stale 'Polling-based' comment missed in round 1 - L5: Align action description with spec line 39027 - M1/L3/L6: Document automation_profile propagation gap in use_action() with TODO for when production code wires the field onto Plan - L4: Add comment explaining local actor name deviation from spec Includes CHANGELOG update describing the integration test scope. ISSUES CLOSED: #771
183 lines
6.0 KiB
Python
183 lines
6.0 KiB
Python
"""ASV benchmarks for DecisionService DI resolution and operations.
|
|
|
|
Measures:
|
|
- Container resolution of DecisionService
|
|
- Decision recording through the service layer
|
|
- Decision tree retrieval through the service layer
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
from datetime import UTC, datetime
|
|
from pathlib import Path
|
|
from unittest.mock import create_autospec
|
|
|
|
try:
|
|
from cleveragents.application.services.decision_service import DecisionService
|
|
from cleveragents.config.settings import Settings
|
|
from cleveragents.domain.models.core.action import Action, ActionState
|
|
from cleveragents.domain.models.core.decision import DecisionType
|
|
from cleveragents.domain.models.core.plan import (
|
|
NamespacedName,
|
|
Plan,
|
|
PlanIdentity,
|
|
PlanPhase,
|
|
PlanTimestamps,
|
|
ProcessingState,
|
|
)
|
|
from cleveragents.infrastructure.database.unit_of_work import UnitOfWork
|
|
except ModuleNotFoundError:
|
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
|
|
from cleveragents.application.services.decision_service import DecisionService
|
|
from cleveragents.config.settings import Settings
|
|
from cleveragents.domain.models.core.action import Action, ActionState
|
|
from cleveragents.domain.models.core.decision import DecisionType
|
|
from cleveragents.domain.models.core.plan import (
|
|
NamespacedName,
|
|
Plan,
|
|
PlanIdentity,
|
|
PlanPhase,
|
|
PlanTimestamps,
|
|
ProcessingState,
|
|
)
|
|
from cleveragents.infrastructure.database.unit_of_work import UnitOfWork
|
|
|
|
|
|
_PLAN_ID = "01HV00000000000000DJBENCH1"
|
|
|
|
|
|
def _make_uow() -> UnitOfWork:
|
|
"""Create a UoW backed by an in-memory SQLite database."""
|
|
uow = UnitOfWork("sqlite:///:memory:")
|
|
uow.init_database()
|
|
return uow
|
|
|
|
|
|
def _seed_prerequisites(uow: UnitOfWork) -> None:
|
|
"""Create the action + plan needed to satisfy FK constraints."""
|
|
with uow.transaction() as ctx:
|
|
action_repo = ctx.actions
|
|
action = Action(
|
|
namespaced_name=NamespacedName.parse("local/bench-di-action"),
|
|
description="Benchmark action",
|
|
definition_of_done="Done",
|
|
strategy_actor="local/s",
|
|
execution_actor="local/e",
|
|
state=ActionState.AVAILABLE,
|
|
created_at=datetime(2026, 1, 1, tzinfo=UTC),
|
|
updated_at=datetime(2026, 1, 1, tzinfo=UTC),
|
|
)
|
|
action_repo.create(action)
|
|
|
|
with uow.transaction() as ctx:
|
|
plan_repo = ctx.lifecycle_plans
|
|
now = datetime(2026, 3, 1, tzinfo=UTC)
|
|
plan = Plan(
|
|
identity=PlanIdentity(plan_id=_PLAN_ID, attempt=1),
|
|
namespaced_name=NamespacedName(namespace="local", name="bench-di-plan"),
|
|
action_name="local/bench-di-action",
|
|
description="Benchmark DI plan",
|
|
definition_of_done="Done",
|
|
phase=PlanPhase.STRATEGIZE,
|
|
processing_state=ProcessingState.PROCESSING,
|
|
strategy_actor="local/s",
|
|
execution_actor="local/e",
|
|
timestamps=PlanTimestamps(created_at=now, updated_at=now),
|
|
created_by="bench",
|
|
tags=[],
|
|
reusable=True,
|
|
read_only=False,
|
|
)
|
|
plan_repo.create(plan)
|
|
|
|
|
|
class TimeDecisionServiceResolution:
|
|
"""Benchmark DecisionService resolution from the DI container."""
|
|
|
|
timeout = 60
|
|
|
|
def setup(self) -> None:
|
|
import os
|
|
|
|
os.environ["CLEVERAGENTS_DATABASE_URL"] = "sqlite:///:memory:"
|
|
from cleveragents.application.container import get_container, reset_container
|
|
|
|
reset_container()
|
|
self.container = get_container()
|
|
|
|
def time_decision_service_resolution(self) -> None:
|
|
self.container.decision_service()
|
|
|
|
def teardown(self) -> None:
|
|
import os
|
|
|
|
from cleveragents.application.container import reset_container
|
|
|
|
reset_container()
|
|
os.environ.pop("CLEVERAGENTS_DATABASE_URL", None)
|
|
|
|
|
|
class TimeDecisionRecord:
|
|
"""Benchmark decision recording through DecisionService."""
|
|
|
|
timeout = 60
|
|
|
|
def setup(self) -> None:
|
|
self.uow = _make_uow()
|
|
_seed_prerequisites(self.uow)
|
|
mock_settings = create_autospec(Settings, instance=True)
|
|
mock_settings.database_url = "sqlite:///:memory:"
|
|
self.svc = DecisionService(settings=mock_settings, unit_of_work=self.uow)
|
|
self._seq = 100
|
|
|
|
def time_decision_record(self) -> None:
|
|
self._seq += 1
|
|
self.svc.record_decision(
|
|
plan_id=_PLAN_ID,
|
|
decision_type=DecisionType.STRATEGY_CHOICE,
|
|
question="Benchmark question?",
|
|
chosen_option="Benchmark option",
|
|
)
|
|
|
|
|
|
class TimeDecisionTreeRetrieval:
|
|
"""Benchmark decision tree retrieval through DecisionService."""
|
|
|
|
timeout = 60
|
|
|
|
def setup(self) -> None:
|
|
self.uow = _make_uow()
|
|
_seed_prerequisites(self.uow)
|
|
mock_settings = create_autospec(Settings, instance=True)
|
|
mock_settings.database_url = "sqlite:///:memory:"
|
|
self.svc = DecisionService(settings=mock_settings, unit_of_work=self.uow)
|
|
|
|
# Build a small tree
|
|
root = self.svc.record_decision(
|
|
plan_id=_PLAN_ID,
|
|
decision_type=DecisionType.PROMPT_DEFINITION,
|
|
question="Benchmark question?",
|
|
chosen_option="Benchmark option",
|
|
)
|
|
|
|
for _ in range(3):
|
|
child = self.svc.record_decision(
|
|
plan_id=_PLAN_ID,
|
|
decision_type=DecisionType.STRATEGY_CHOICE,
|
|
question="Benchmark question?",
|
|
chosen_option="Benchmark option",
|
|
parent_decision_id=root.decision_id,
|
|
)
|
|
for _ in range(2):
|
|
self.svc.record_decision(
|
|
plan_id=_PLAN_ID,
|
|
decision_type=DecisionType.IMPLEMENTATION_CHOICE,
|
|
question="Benchmark question?",
|
|
chosen_option="Benchmark option",
|
|
parent_decision_id=child.decision_id,
|
|
)
|
|
|
|
def time_decision_tree_retrieval(self) -> None:
|
|
self.svc.get_tree(_PLAN_ID)
|