forked from HAL9000/cleveragents-core
1878998b7a
Rename the TDD tag system from tdd_bug/tdd_bug_<N> to tdd_issue/tdd_issue_<N> across the entire codebase. The tdd_expected_fail tag is unchanged. The TDD expected-failure workflow is not limited to bug fixes — it applies equally to any issue type (features, tasks, refactors). The _bug suffix was misleading and narrowed the perceived scope. The new _issue suffix accurately reflects that the TDD tagging system applies to any Forgejo issue. Changes span 92 files: - features/environment.py: validate_tdd_tags(), should_invert_result(), and apply_tdd_inversion() updated — regex, variables, error messages - robot/tdd_expected_fail_listener.py: _validate_tdd_tags(), _should_invert_result(), start_test(), end_test() updated consistently - 33 Behave .feature files: all @tdd_bug/@tdd_bug_<N> tags renamed - 29 Robot .robot files: all tdd_bug/tdd_bug_<N> tags renamed - 3 Robot fixture files renamed (tdd_bug_alone, tdd_missing_tdd_bug, tdd_expected_fail_missing_bug_n) with content and references updated - Tag validation tests and helpers updated (function names, command dispatch keys, output strings, fixture references) - CONTRIBUTING.md: section renamed from 'TDD Bug Test Tags' to 'TDD Issue Test Tags', all tag references and examples updated - noxfile.py: comment references updated - Step definition files, mock helpers, and benchmark files: docstring references updated ISSUES CLOSED: #965
186 lines
6.9 KiB
Python
186 lines
6.9 KiB
Python
"""Step definitions for TDD Issue #1024 — SQLite DB URL CWD resolution.
|
|
|
|
These steps verify that when ``CLEVERAGENTS_HOME`` is set to a temporary
|
|
directory, the SQLite database file is created **inside** that directory
|
|
rather than relative to the current working directory.
|
|
|
|
The default ``database_url`` in ``Settings`` is ``sqlite:///cleveragents.db``
|
|
— a relative path that resolves against CWD. This breaks test isolation
|
|
when ``CLEVERAGENTS_HOME`` points to a different directory.
|
|
|
|
Bug: https://git.cleverthis.com/cleveragents/cleveragents-core/issues/1024
|
|
TDD: https://git.cleverthis.com/cleveragents/cleveragents-core/issues/1034
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
import os
|
|
import shutil
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
from behave import given, then, when
|
|
from behave.runner import Context
|
|
|
|
from cleveragents.application.container import get_database_url, reset_container
|
|
from cleveragents.config.settings import Settings
|
|
|
|
|
|
def _suppress_structlog_stdout() -> tuple[int, bool]:
|
|
"""Prevent structlog debug lines from contaminating CLI stdout."""
|
|
import structlog
|
|
|
|
root = logging.getLogger()
|
|
prev_level = root.level
|
|
root.setLevel(logging.WARNING)
|
|
|
|
prev_config = structlog.get_config()
|
|
prev_cache: bool = prev_config.get("cache_logger_on_first_use", True) # type: ignore[assignment]
|
|
structlog.configure(
|
|
logger_factory=structlog.stdlib.LoggerFactory(),
|
|
wrapper_class=structlog.stdlib.BoundLogger,
|
|
cache_logger_on_first_use=False,
|
|
)
|
|
|
|
return prev_level, prev_cache
|
|
|
|
|
|
def _restore_structlog(prev_level: int, prev_cache: bool) -> None:
|
|
"""Undo the suppression applied by :func:`_suppress_structlog_stdout`."""
|
|
import structlog
|
|
|
|
logging.getLogger().setLevel(prev_level)
|
|
structlog.configure(cache_logger_on_first_use=prev_cache)
|
|
|
|
|
|
def _extract_sqlite_path(url: str) -> Path | None:
|
|
"""Extract the file path from a SQLite URL.
|
|
|
|
Handles both ``sqlite:///path`` (relative) and
|
|
``sqlite:////abs/path`` (absolute) forms.
|
|
"""
|
|
prefix = "sqlite:///"
|
|
if not url.startswith(prefix):
|
|
return None
|
|
raw_path = url[len(prefix) :]
|
|
if not raw_path:
|
|
return None
|
|
return Path(raw_path).resolve()
|
|
|
|
|
|
@given("a fresh CLEVERAGENTS_HOME temp directory for DB URL testing")
|
|
def step_fresh_home(context: Context) -> None:
|
|
"""Create an isolated CLEVERAGENTS_HOME and record the original CWD."""
|
|
# Reset singletons to avoid stale state
|
|
reset_container()
|
|
Settings._instance = None # type: ignore[attr-defined]
|
|
|
|
prev_level, prev_cache = _suppress_structlog_stdout()
|
|
|
|
context.original_cwd = Path.cwd().resolve()
|
|
context.tdd_home = tempfile.mkdtemp(prefix="tdd_sqlite_url_1024_")
|
|
|
|
# Remove any env vars that could override database_url
|
|
context.saved_db_url = os.environ.pop("CLEVERAGENTS_DATABASE_URL", None)
|
|
context.saved_test_db_url = os.environ.pop("CLEVERAGENTS_TEST_DATABASE_URL", None)
|
|
|
|
os.environ["CLEVERAGENTS_HOME"] = context.tdd_home
|
|
|
|
# Reset singletons so new Settings picks up the environment
|
|
reset_container()
|
|
Settings._instance = None # type: ignore[attr-defined]
|
|
|
|
def _cleanup() -> None:
|
|
# Restore env vars
|
|
if context.saved_db_url is not None:
|
|
os.environ["CLEVERAGENTS_DATABASE_URL"] = context.saved_db_url
|
|
else:
|
|
os.environ.pop("CLEVERAGENTS_DATABASE_URL", None)
|
|
if context.saved_test_db_url is not None:
|
|
os.environ["CLEVERAGENTS_TEST_DATABASE_URL"] = context.saved_test_db_url
|
|
else:
|
|
os.environ.pop("CLEVERAGENTS_TEST_DATABASE_URL", None)
|
|
|
|
os.environ.pop("CLEVERAGENTS_HOME", None)
|
|
reset_container()
|
|
Settings._instance = None # type: ignore[attr-defined]
|
|
|
|
shutil.rmtree(context.tdd_home, ignore_errors=True)
|
|
_restore_structlog(prev_level, prev_cache)
|
|
|
|
context.add_cleanup(_cleanup)
|
|
|
|
|
|
@when("I resolve the effective database URL from Settings")
|
|
def step_resolve_db_url(context: Context) -> None:
|
|
"""Resolve the effective database URL via the container helper."""
|
|
# Use the container's get_database_url which is what the app uses
|
|
context.resolved_url = get_database_url()
|
|
context.resolved_path = _extract_sqlite_path(context.resolved_url)
|
|
|
|
|
|
@when("I resolve the Settings database_url default")
|
|
def step_resolve_settings_default(context: Context) -> None:
|
|
"""Resolve the database_url via the Settings model default.
|
|
|
|
Constructs a fresh Settings instance (with CLEVERAGENTS_DATABASE_URL
|
|
removed) to exercise the default ``database_url`` field, then resolves
|
|
the path. This tests the Settings-level default independently of the
|
|
container's ``get_database_url()`` helper.
|
|
"""
|
|
settings = Settings()
|
|
context.settings_db_url = settings.get_database_url()
|
|
context.settings_db_path = _extract_sqlite_path(context.settings_db_url)
|
|
|
|
|
|
@then("the resolved database path should be inside CLEVERAGENTS_HOME")
|
|
def step_path_inside_home(context: Context) -> None:
|
|
"""Assert the database path is under CLEVERAGENTS_HOME."""
|
|
home = Path(context.tdd_home).resolve()
|
|
db_path = context.resolved_path
|
|
assert db_path is not None, (
|
|
f"Could not extract a file path from database URL: {context.resolved_url}"
|
|
)
|
|
assert str(db_path).startswith(str(home)), (
|
|
f"Database path {db_path} is NOT inside CLEVERAGENTS_HOME {home}.\n"
|
|
f"Full database URL: {context.resolved_url}"
|
|
)
|
|
|
|
|
|
@then("the resolved database path should not be inside the original CWD")
|
|
def step_path_not_in_cwd(context: Context) -> None:
|
|
"""Assert the database path is NOT under the original CWD."""
|
|
cwd = context.original_cwd
|
|
db_path = context.resolved_path
|
|
assert db_path is not None, (
|
|
f"Could not extract a file path from database URL: {context.resolved_url}"
|
|
)
|
|
# The DB should not resolve to a child of the original CWD
|
|
# (unless CLEVERAGENTS_HOME happens to be inside CWD, which it isn't
|
|
# for our temp directory setup).
|
|
home = Path(context.tdd_home).resolve()
|
|
if not str(home).startswith(str(cwd)):
|
|
# Only check if home is NOT under cwd (normal case)
|
|
assert not str(db_path).startswith(str(cwd)), (
|
|
f"Database path {db_path} is inside CWD {cwd} instead of "
|
|
f"CLEVERAGENTS_HOME {home}.\n"
|
|
f"Full database URL: {context.resolved_url}"
|
|
)
|
|
|
|
|
|
@then("the settings database path should be inside CLEVERAGENTS_HOME")
|
|
def step_settings_path_inside_home(context: Context) -> None:
|
|
"""Assert the Settings-level database_url resolves inside CLEVERAGENTS_HOME."""
|
|
home = Path(context.tdd_home).resolve()
|
|
db_path = context.settings_db_path
|
|
assert db_path is not None, (
|
|
f"Could not extract a file path from Settings database URL: "
|
|
f"{context.settings_db_url}"
|
|
)
|
|
assert str(db_path).startswith(str(home)), (
|
|
f"Settings database path {db_path} is NOT inside "
|
|
f"CLEVERAGENTS_HOME {home}.\n"
|
|
f"Full Settings database URL: {context.settings_db_url}"
|
|
)
|