16dc4ab18d
CI / lint (push) Successful in 16s
CI / build (push) Successful in 16s
CI / quality (push) Successful in 19s
CI / security (push) Successful in 34s
CI / typecheck (push) Successful in 38s
CI / benchmark-regression (push) Has been skipped
CI / unit_tests (push) Successful in 2m24s
CI / docker (push) Successful in 40s
CI / integration_tests (push) Successful in 3m20s
CI / coverage (push) Successful in 5m25s
CI / benchmark-publish (push) Successful in 16m50s
## Summary Fix `agents project show` not finding a project immediately after creation. Extends the `session.commit()` fix from #589 to also cover `update()` and `delete()` in `NamespacedProjectRepository`. ## Changes **Production fix** (`src/cleveragents/infrastructure/database/repositories.py`): - Add `session.commit()` to `create()`, `update()`, and `delete()` methods - Add `finally: session.close()` guard to all three methods - Update class docstring to reflect commit-per-method pattern **Tests & benchmarks**: - 3 Behave BDD regression scenarios (`features/project_show_after_create.feature`) - Robot Framework integration smoke tests with "not found" assertion (`robot/project_show_after_create.robot`) - ASV benchmarks for create-then-show round-trip (`benchmarks/project_show_after_create_bench.py`) ## Review feedback addressed - **F1**: Removed unrelated em-dash CHANGELOG edits — wrote clean entry from scratch - **F2**: Kept Suite Setup/Teardown (required for `${PYTHON}` variable); updated stale docs - **F3**: Added "not found" string assertion to Robot negative test case - **F4**: Removed redundant `Base.metadata.create_all()` from `_make_fresh_repo()` helper - Updated all stale TDD "expected to fail" comments — this PR includes the fix ## Process - Single squashed commit, rebased onto `master` (no merge commits) - Prescribed commit message from issue #590 metadata ISSUES CLOSED: #590 Reviewed-on: #593 Reviewed-by: Rui Hu <rui.hu@cleverthis.com> Co-authored-by: Brent E. Edwards <brent.edwards@cleverthis.com> Co-committed-by: Brent E. Edwards <brent.edwards@cleverthis.com>
116 lines
3.7 KiB
Python
116 lines
3.7 KiB
Python
"""ASV benchmarks for project create + show round-trip.
|
|
|
|
Measures the cost of creating a project and then showing it via the
|
|
``NamespacedProjectRepository``, using a file-based SQLite database so
|
|
that each operation gets a fresh session (matching real CLI behaviour).
|
|
|
|
Targets bug #590 — ``create()`` now calls ``session.commit()`` so data
|
|
is visible to a subsequent ``get()`` in a separate session.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import contextlib
|
|
import importlib
|
|
import os
|
|
import shutil
|
|
import sys
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
# 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)
|
|
|
|
# Force-reload so ASV picks up the source tree version.
|
|
import cleveragents # noqa: E402
|
|
|
|
importlib.reload(cleveragents)
|
|
|
|
from sqlalchemy import create_engine # noqa: E402
|
|
from sqlalchemy.orm import sessionmaker # noqa: E402
|
|
from sqlalchemy.pool import NullPool # noqa: E402
|
|
|
|
from cleveragents.domain.models.core.project import ( # noqa: E402
|
|
NamespacedProject,
|
|
parse_namespaced_name,
|
|
)
|
|
from cleveragents.infrastructure.database.models import Base # noqa: E402
|
|
from cleveragents.infrastructure.database.repositories import ( # noqa: E402
|
|
NamespacedProjectRepository,
|
|
ProjectNotFoundError,
|
|
)
|
|
|
|
|
|
def _fresh_repo(db_path: str) -> NamespacedProjectRepository:
|
|
"""Return a repo backed by a *new* engine/session for *db_path*.
|
|
|
|
``NullPool`` ensures SQLite file handles are released immediately
|
|
when the engine is no longer referenced.
|
|
"""
|
|
engine = create_engine(f"sqlite:///{db_path}", echo=False, poolclass=NullPool)
|
|
Base.metadata.create_all(engine)
|
|
factory = sessionmaker(bind=engine, expire_on_commit=False)
|
|
return NamespacedProjectRepository(session_factory=factory)
|
|
|
|
|
|
class ProjectShowAfterCreateSuite:
|
|
"""Benchmark create-then-show round trip with separate sessions."""
|
|
|
|
timeout = 30.0
|
|
|
|
def setup(self) -> None:
|
|
self._tmpdir = tempfile.mkdtemp(prefix="bench_show_590_")
|
|
self._db_path = os.path.join(self._tmpdir, "bench.db")
|
|
engine = create_engine(
|
|
f"sqlite:///{self._db_path}",
|
|
echo=False,
|
|
poolclass=NullPool,
|
|
)
|
|
Base.metadata.create_all(engine)
|
|
engine.dispose()
|
|
self._counter = 0
|
|
|
|
def teardown(self) -> None:
|
|
shutil.rmtree(self._tmpdir, ignore_errors=True)
|
|
|
|
def time_create_then_show(self) -> None:
|
|
"""Create a project in one session, show (get) in another."""
|
|
self._counter += 1
|
|
name = f"bench-show-{self._counter}"
|
|
parsed = parse_namespaced_name(name)
|
|
proj = NamespacedProject(name=parsed.name, namespace=parsed.namespace)
|
|
|
|
create_repo = _fresh_repo(self._db_path)
|
|
create_repo.create(proj)
|
|
|
|
show_repo = _fresh_repo(self._db_path)
|
|
with contextlib.suppress(ProjectNotFoundError):
|
|
show_repo.get(f"local/{name}")
|
|
|
|
def track_show_after_create_found(self) -> int:
|
|
"""Track whether show finds the created project.
|
|
|
|
Returns 1 when get() successfully finds the project; 0 if
|
|
ProjectNotFoundError is raised.
|
|
"""
|
|
self._counter += 1
|
|
name = f"bench-show-track-{self._counter}"
|
|
parsed = parse_namespaced_name(name)
|
|
proj = NamespacedProject(name=parsed.name, namespace=parsed.namespace)
|
|
|
|
create_repo = _fresh_repo(self._db_path)
|
|
create_repo.create(proj)
|
|
|
|
show_repo = _fresh_repo(self._db_path)
|
|
try:
|
|
show_repo.get(f"local/{name}")
|
|
return 1
|
|
except ProjectNotFoundError:
|
|
return 0
|
|
|
|
|
|
ProjectShowAfterCreateSuite.track_show_after_create_found.unit = "found"
|