d0689573e0
CI / lint (pull_request) Successful in 13s
CI / benchmark-publish (pull_request) Has been skipped
CI / quality (pull_request) Successful in 18s
CI / build (pull_request) Successful in 32s
CI / security (pull_request) Successful in 48s
CI / typecheck (pull_request) Successful in 55s
CI / unit_tests (pull_request) Failing after 2m41s
CI / docker (pull_request) Has been skipped
CI / integration_tests (pull_request) Successful in 3m25s
CI / coverage (pull_request) Successful in 5m56s
CI / benchmark-regression (pull_request) Successful in 33m35s
Add TDD regression tests for bug #570 where `_get_session_service()` calls `container.db()` but the DI `Container` class has no `db` provider, raising `AttributeError`. Same root cause as bug #554. Includes 4 Behave BDD scenarios tagged `@tdd_bug @tdd_bug_570 @tdd_expected_fail`, Robot Framework integration smoke tests with `--format plain`, and ASV service-layer benchmarks. Tests exercise the real DI path by resetting `_service = None` and using a file-based SQLite database. Implements the `@tdd_expected_fail` inversion infrastructure: - Behave: `after_scenario` hook in `features/environment.py` inverts pass/fail for scenarios tagged `@tdd_expected_fail` - Robot: `robot/tdd_expected_fail_listener.py` listener (API v3) performs the same inversion for Robot test cases - `noxfile.py`: registers the listener via `--listener` in both the `integration_tests` and `slow_integration_tests` sessions Migrates 18 existing TDD scenarios across 5 feature files from the old `@tdd @bugNNN` convention to the standardised `@tdd_bug @tdd_bug_NNN` tags per CONTRIBUTING.md § TDD Bug Test Tags. Refs: #570
101 lines
3.5 KiB
Python
101 lines
3.5 KiB
Python
"""ASV benchmarks for session create service-layer performance (bug #570).
|
|
|
|
Measures the cost of creating a session through ``PersistentSessionService``
|
|
using a file-based SQLite database so that each operation exercises the full
|
|
service layer (repository -> SQLAlchemy -> SQLite round-trip).
|
|
|
|
Note: these benchmarks construct ``PersistentSessionService`` directly and do
|
|
**not** exercise the DI container wiring path (``_get_session_service`` /
|
|
``container.db()``). Their purpose is to establish a service-layer create
|
|
performance baseline so regressions can be detected after the bug-fix lands.
|
|
Same root cause as bug #554.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import importlib
|
|
import os
|
|
import shutil
|
|
import sys
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
_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 sqlalchemy import create_engine # noqa: E402
|
|
from sqlalchemy.orm import sessionmaker # noqa: E402
|
|
|
|
from cleveragents.application.services.session_service import ( # noqa: E402
|
|
PersistentSessionService,
|
|
)
|
|
from cleveragents.infrastructure.database.models import Base # noqa: E402
|
|
from cleveragents.infrastructure.database.repositories import ( # noqa: E402
|
|
SessionMessageRepository,
|
|
SessionRepository,
|
|
)
|
|
|
|
|
|
class SessionCreateDISuite:
|
|
"""Benchmark session create through the service layer.
|
|
|
|
Engine and sessionmaker are built once in ``setup()`` and reused across
|
|
benchmark iterations to measure service-layer cost without engine
|
|
construction overhead.
|
|
"""
|
|
|
|
timeout = 60
|
|
|
|
def setup(self) -> None:
|
|
self._tmpdir = tempfile.mkdtemp(prefix="bench_sce_570_")
|
|
self._db_path = os.path.join(self._tmpdir, "bench.db")
|
|
self._engine = create_engine(f"sqlite:///{self._db_path}", echo=False)
|
|
Base.metadata.create_all(self._engine)
|
|
self._session_factory = sessionmaker(bind=self._engine, expire_on_commit=False)
|
|
|
|
def teardown(self) -> None:
|
|
self._engine.dispose()
|
|
shutil.rmtree(self._tmpdir, ignore_errors=True)
|
|
|
|
def _make_service(self) -> PersistentSessionService:
|
|
"""Build a PersistentSessionService using the shared session factory."""
|
|
return PersistentSessionService(
|
|
session_repo=SessionRepository(session_factory=self._session_factory),
|
|
message_repo=SessionMessageRepository(
|
|
session_factory=self._session_factory
|
|
),
|
|
)
|
|
|
|
def time_create_session(self) -> None:
|
|
"""Create a session via the service layer."""
|
|
svc = self._make_service()
|
|
svc.create()
|
|
|
|
def time_create_with_actor(self) -> None:
|
|
"""Create a session with a custom actor."""
|
|
svc = self._make_service()
|
|
svc.create(actor_name="openai/gpt-4")
|
|
|
|
def track_create_persists(self) -> int:
|
|
"""Track session create persistence at the service layer.
|
|
|
|
Returns the count of sessions created via the service. This
|
|
benchmark constructs ``PersistentSessionService`` directly,
|
|
bypassing the DI container (``_get_session_service`` /
|
|
``container.db()``). It therefore does **not** reproduce bug
|
|
#570 — its purpose is to establish a service-layer persistence
|
|
baseline so regressions can be detected after the fix lands.
|
|
"""
|
|
svc = self._make_service()
|
|
svc.create(actor_name="bench/create-test")
|
|
sessions = svc.list()
|
|
return len(sessions)
|
|
|
|
|
|
SessionCreateDISuite.track_create_persists.unit = "sessions"
|