Files
temp/robot/helper_tdd_sqlite_url_cwd.py
freemo e52c79e958 test: add TDD bug-capture test for #1024 — SQLite DB URL CWD resolution
Add Behave BDD scenarios (tagged @tdd_bug @tdd_bug_1024 @tdd_expected_fail)
that verify the default database_url resolves inside CLEVERAGENTS_HOME rather
than the current working directory.  Two scenarios exercise both the container
get_database_url() helper and the Settings model default.

Add Robot Framework integration tests with a helper script exercising the same
resolution paths via subprocess, verifying database URL resolution and CLI
database file placement relative to CLEVERAGENTS_HOME.

The tests currently fail as expected because the bug in #1024 is still present:
the relative SQLite path sqlite:///cleveragents.db resolves against CWD.  The
@tdd_expected_fail tag inverts the result so CI passes.

ISSUES CLOSED: #1034
2026-03-24 20:29:49 +00:00

203 lines
6.9 KiB
Python

"""Helper script for tdd_sqlite_url_cwd.robot integration tests.
Each subcommand exercises the real application path (no mocks) to
reproduce bug #1024. The helper reports the **real** outcome: it exits
0 and prints the sentinel when the database resolves inside
CLEVERAGENTS_HOME (bug is fixed), and exits 1 when it resolves to CWD
(bug still present). The ``tdd_expected_fail_listener`` on the Robot
side handles pass/fail inversion while the bug remains open.
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 os
import shutil
import sys
import tempfile
from collections.abc import Callable
from pathlib import Path
from typing import NoReturn
# Ensure local source tree is importable.
_ROOT = Path(__file__).resolve().parents[1]
_SRC = str(_ROOT / "src")
if _SRC not in sys.path:
sys.path.insert(0, _SRC)
def _fail(message: str) -> NoReturn:
"""Print an error message to stderr and exit with code 1."""
print(message, file=sys.stderr)
sys.exit(1)
def _extract_sqlite_path(url: str) -> Path | None:
"""Extract the file path from a SQLite URL."""
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()
# ---------------------------------------------------------------------------
# Subcommands
# ---------------------------------------------------------------------------
def check_db_url_resolution() -> None:
"""Verify that get_database_url() resolves inside CLEVERAGENTS_HOME.
Creates a temporary CLEVERAGENTS_HOME, removes any overriding env
vars for database URLs, resets singletons, and checks whether the
resolved database URL points inside the temporary home directory.
"""
from cleveragents.application.container import (
get_database_url,
reset_container,
)
from cleveragents.config.settings import Settings
original_cwd = Path.cwd().resolve()
tmpdir = tempfile.mkdtemp(prefix="tdd_sqlite_url_1024_robot_")
# Save and remove any overriding env vars
saved_db_url = os.environ.pop("CLEVERAGENTS_DATABASE_URL", None)
saved_test_db_url = os.environ.pop("CLEVERAGENTS_TEST_DATABASE_URL", None)
try:
os.environ["CLEVERAGENTS_HOME"] = tmpdir
reset_container()
Settings._instance = None
resolved_url = get_database_url()
db_path = _extract_sqlite_path(resolved_url)
home = Path(tmpdir).resolve()
if db_path is None:
_fail(f"Could not extract file path from database URL: {resolved_url}")
if not str(db_path).startswith(str(home)):
_fail(
f"Database path {db_path} is NOT inside "
f"CLEVERAGENTS_HOME {home}.\n"
f"Full URL: {resolved_url}\n"
f"CWD was: {original_cwd}"
)
print("tdd-sqlite-url-cwd-resolution-ok")
finally:
# Restore env vars
if saved_db_url is not None:
os.environ["CLEVERAGENTS_DATABASE_URL"] = saved_db_url
else:
os.environ.pop("CLEVERAGENTS_DATABASE_URL", None)
if saved_test_db_url is not None:
os.environ["CLEVERAGENTS_TEST_DATABASE_URL"] = saved_test_db_url
else:
os.environ.pop("CLEVERAGENTS_TEST_DATABASE_URL", None)
os.environ.pop("CLEVERAGENTS_HOME", None)
reset_container()
Settings._instance = None
shutil.rmtree(tmpdir, ignore_errors=True)
def check_cli_db_location() -> None:
"""Verify that a CLI command creates the DB inside CLEVERAGENTS_HOME.
Invokes ``session list`` via the Typer CLI runner and then checks
whether any database files ended up inside CLEVERAGENTS_HOME rather
than the original CWD.
"""
from typer.testing import CliRunner
from cleveragents.application.container import reset_container
from cleveragents.cli.commands.session import app as session_app
from cleveragents.config.settings import Settings
original_cwd = Path.cwd().resolve()
tmpdir = tempfile.mkdtemp(prefix="tdd_sqlite_url_1024_robot_cli_")
saved_db_url = os.environ.pop("CLEVERAGENTS_DATABASE_URL", None)
saved_test_db_url = os.environ.pop("CLEVERAGENTS_TEST_DATABASE_URL", None)
try:
os.environ["CLEVERAGENTS_HOME"] = tmpdir
reset_container()
Settings._instance = None
runner = CliRunner()
runner.invoke(session_app, ["list"])
home = Path(tmpdir).resolve()
# Check for DB files in CWD that shouldn't be there
suspect_files = [
original_cwd / "cleveragents.db",
original_cwd / "cleveragents_test.db",
original_cwd / ".cleveragents" / "db.sqlite",
]
found_in_cwd = [f for f in suspect_files if f.exists()]
if found_in_cwd:
_fail(
f"Database file(s) found in CWD instead of CLEVERAGENTS_HOME:\n"
f" CWD files: {found_in_cwd}\n"
f" CLEVERAGENTS_HOME: {home}"
)
# Verify the DB was actually created inside CLEVERAGENTS_HOME.
# If no DB file exists anywhere, the test isn't exercising the
# database path and the result would be vacuously true.
home_db_files = list(home.rglob("*.db")) + list(home.rglob("*.sqlite"))
if not home_db_files:
_fail(
f"No database file was created inside CLEVERAGENTS_HOME "
f"({home}). The CLI command did not trigger database "
f"creation, so the test result is inconclusive.\n"
f"CWD was: {original_cwd}"
)
print("tdd-sqlite-url-cwd-cli-ok")
finally:
if saved_db_url is not None:
os.environ["CLEVERAGENTS_DATABASE_URL"] = saved_db_url
else:
os.environ.pop("CLEVERAGENTS_DATABASE_URL", None)
if saved_test_db_url is not None:
os.environ["CLEVERAGENTS_TEST_DATABASE_URL"] = saved_test_db_url
else:
os.environ.pop("CLEVERAGENTS_TEST_DATABASE_URL", None)
os.environ.pop("CLEVERAGENTS_HOME", None)
reset_container()
Settings._instance = None
shutil.rmtree(tmpdir, ignore_errors=True)
# ---------------------------------------------------------------------------
# Dispatcher
# ---------------------------------------------------------------------------
_COMMANDS: dict[str, Callable[[], None]] = {
"check-db-url-resolution": check_db_url_resolution,
"check-cli-db-location": check_cli_db_location,
}
if __name__ == "__main__":
if len(sys.argv) < 2 or sys.argv[1] not in _COMMANDS:
print(
f"Usage: {sys.argv[0]} <{'|'.join(_COMMANDS)}>",
file=sys.stderr,
)
sys.exit(1)
cmd = _COMMANDS[sys.argv[1]]
cmd()