forked from HAL9000/cleveragents-core
e52c79e958
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
203 lines
6.9 KiB
Python
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()
|